Configurations
Configurations & Options made easy.
Installation
Configurations is a Julia Language package. To install Configurations, please open Julia's interactive session (known as REPL) and press ] key in the REPL to use the package mode, then type the following command
pkg> add Configurations
Usage
This package provides a macro @option
to let you define struct
s to represent options/configurations, and serialize between different option/configuration file format.
Configurations.@option
— Macro@option [alias::String] <struct def>
Define an option struct type. This will auto-generate methods that parse a given Dict{String}
object (the keys must be of type String
) into an instance of the struct type you defined. One can use alias
string to distinguish multiple possible option type for the same field.
Special Types
Maybe{T}
: this type is equivalent toUnion{Nothing, T}
and is treated specially in@option
, it will always have a default value ofnothing
if not specified.Reflect
: this type is treated specially to allow one to use a field to store the corresponding type information.
from v0.16.0 Configurations stops overloading the Base.show
method for you, if you need pretty printing of your option types, consider overloading the Base.show(io::IO, mime::MIME, x)
method to pprint_struct(io, mime, x)
provided by GarishPrint
from v0.12.0 the field alias feature is removed due to the syntax conflict with field docstring. Please refer to #17.
Example
One can define option type via @option
macro with or without an alias.
julia> "Option A"
@option "option_a" struct OptionA
name::String
int::Int = 1
end
julia> "Option B"
@option "option_b" struct OptionB
opt::OptionA = OptionA(;name = "Sam")
float::Float64 = 0.3
end
julia> option = from_dict(OptionB, d)
OptionB(;
opt = OptionA(;
name = "Roger",
int = 2,
),
float = 0.33,
)
when there are multiple possible option type for one field, one can use the alias to distinguish them
julia> @option struct OptionD
opt::Union{OptionA, OptionB}
end
julia> d1 = Dict{String, Any}(
"opt" => Dict{String, Any}(
"option_b" => d
)
);
julia> from_dict(OptionD, d1)
OptionD(;
opt = OptionB(;
opt = OptionA(;
name = "Roger",
int = 2,
),
float = 0.33,
),
)
Frequently Asked Questions
- When should I use this package?
When you have a lot settings/preferences/keyword arguments for a package or a function, or you need to validate a JSON schema, a REST API for your web server. A similar package in Python is pydantic in Python, but this package only provides the basic feature, a pydantic compatible package will be developed in the future in KungIChi.
A common type of code is instead of writing many keyword arguments, like foo(;kw_a=1, kw_b=2, kw_c, ...)
, wrap them in an option type
@option struct FooOptions
kw_a::Int = 1
kw_b::Int = 2
# ...
end
foo(x, y;kw...) = foo(x, y, FooOptions(;kw...))
foo(x, y, options::FooOptions) = #= actual implementation =#
this will make your keyword argument easy to read and serialize with readable markup language like TOML.
- Why Configurations only supports TOML?
This is not true, Configurations supports converting a dictionary type (subtype of AbstractDict{String}
) to option types defined by @option
. The reason why TOML is supported by default is because Julia is shipped with a TOML parser already, so we can support TOML without adding extra dependency. And depending on other format parsers such as YAML
, JSON
etc. will cause an extra loading latency that is not necessary for most of the users who is fine with just TOML.
On the other hand, Configurations
aims to be lightweight because it is used by latency sensitive packages like Comonicon. We will put other features into KungIChi in the future (it is still work-in-progress).
- Why do you need an
@option
macro?
the @option
macro provides the functionality of reflection in compile time, e.g we support type alias and default value reflection. These feature is not implementable without macros.
- Why not just use a supertype but a macro?
besides the reason in the previous question, for a specific project, we can write a supertype and implement a set of generic interface, which is fine. But as a package, we need to make things composable and generic, thus, we do not want to block users from defining their own supertype. In this package, we use traits instead of supertypes, this makes things composable, e.g you can use the option types defined in Pluto as part of your own option types.
- What is the difference between this package and Preferences
Preferences aims to provide a mechanism of reading package preferences that works with the package manager Pkg, but this package aims to provide a mechnism to read a setting/preference to Julia structs. Thus these two are completely orthogonal packages and they can work together.
- What is the difference between this package and StructTypes
StructTypes is mainly used to provide a standard interface to parse dict-like data to a Julia struct via traits to make parsing faster, but this package aims to support the mapping between a dict-like data and a specific kind of Julia struct defined by @option
which provides limited semantic that is not as general as a normal Julia struct (it is closer to Base.@kwdef
semantic). And we have plans of supporting StructTypes traits by default once JuliaData/StructTypes#53 is figured out.
License
MIT License