Advanced Usage
Contents
Most of my packages use the same boilerplate to setup everything in a way that allows easy access propogation of user provided parameters:
using BetterInputFiles
using ArgParse
function get_args()
s = ArgParseSettings()
@add_arg_table s begin
"--verbose", "-v"
help = "Increase level of logging verbosity"
action = :store_true
"input"
help = "Path to input file"
required = true
end
return parse_args(s)
end
function main()
args = get_args()
verbose = args["verbose"]
input_path = args["input"]
input = setup_input(toml, verbose)
@debug "Read in $input_path"
run_MyPackage(input) # Actually run everything with the prepared `input`
end
Given an input file, this will produce a dictionary with a global
key containing a dictionary with
input_path
: The directory the input file is stored inbase_path
: A base path defined relative toinput_path
from which any relative paths used by the package will be relative tooutput_path
: The output directory where all output, including logging, will be putlogging
: Whether logging was set up. This allows you to use my BetterInputFiles logging function or your ownlog_file
: If using my logging function, then in addition tostdout
, logs will be saved tolog_file
Input file options
Much of the behaviour can be controlled from the input file itself, without needed to change any code. Note that all of these examples are written in TOML, but will work for YAML, and JSON files as well.
Change default paths
By default, BetterInputFiles sets base_path
equal to input_path
, and output_path
equal to base_path/Output
. By defining your own (absolute or relative) base_path
, and output_path
, you can change these defaults. Note that base_path
is defined relative to input_path
, and output_path
is defined relative to base_path
, but absolute paths are respected.
[ global ]
base_path = "../Examples"
# base_path = input_path/../Examples
output_path = "/tmp/test"
# output_path = "/tmp/test"
Change logging behaviour
You can define in the input file, whether you want to use BetterInputFiles logging or not. Additionally you can specify a new location for the log file (defaults to output_path/log.txt
). This can be either absolute, or relative to output_path
.
[ global ]
logging = false
log_file = logs/out.txt
# log_file = output_path/logs/out.txt
Include another input file
It is sometimes helpful to seperate your input into a number of different files, or to use the same input options in a number of different files.
# file_1.toml
[ key1 ]
a = 1
b = 2
# file_2.toml
<include file_1.toml>
[ key2 ]
c = 3
d = 4
input = setup_input("file_2.toml", verbose)
Will produce:
[ key1 ]
a = 1
b = 2
[ key2 ]
c = 3
d = 4
Interpolate Environmental Variables
You can easily interpolate environmental variables via:
[ key1 ]
a = <$A>
b = <$B>
Generic Interpolation
You can also interpolate other keys, as long as they belong to the same subtree, allowing for easy duplication.
[ key1 ]
a = 1
b = <%a>
Propogate defaults
You can specify default values which will be available under every key. These are also quite useful when combined with interpolation, as this default value will be part of every base key. If a default key is specified already, then the default will be ignored
[ DEFAULT ]
a = 1
b = 2
[ key1 ]
c = <%a>
d = <%b>
Automatic upper-case
Finally, BetterInputFiles will automatically make every key upper-case, so your user's can ignore case when specifying keys. Values are still case-specific.
Script options
In addition to changing behaviour from the input file, there's also a lot of functionality BetterInputFiles provides when writing scripts
Add new global paths
By defining an OrderedDict{String, Tuple{String, String}}
of paths, you can add new global paths, which can be accessed throughout the package. The following creates data_path
relative to base_path
, and filter_path
relative to data_path
. You must provide defaults for these paths but the user can overwrite then in the .toml
(see Change default paths).
paths = Dict(
# Name of path => (relative parent, default)
"data_path" => ("base_path", "Data")
"filter_path" => ("data_path", "Filters")
input = setup_input(input_path, verbose; paths=paths)
Custom Metadata
By providing BetterInputFiles with a Vector{Tuple{String, String}}
, you can add additional metadata to the input file, in addition to the date of creation and original input file.
custom_metadata = [("Custom", "Metadata")]
input = setup_input(input_path, verbose; custom_metadata=custom_metadata)
This will create a new key="Custom"
and value="Metadata"
in the Metadata
dictionary.
Manually set extension
Sometimes you will have an input file which acts like a TOML, YAML, or JSON file, but has a different extension. If this is the case, you can let BetterInputFiles know which extension to assume.
input = setup_input("/path/to/input.example", verbose, "toml")