Examples of basic usage
There are many uses of rickle
, and some of the functionality is described here through examples.
Simple Config
The most basic usage of a rickle
is to use it as a config object. Let's create a scenario in which this might be useful.
Say you have a common API served through a FastAPI app and you need 10 versions of the API, each having the same code base but with different databases in the back, and some different endpoint configurations.
Let's make our first simple config in YAML, call it config_US.yaml
.
config_US.yaml | |
---|---|
As an example, we will have the simple API:
Here we can see that the config YAML file is loaded as a rickle
. In the creation of the FastAPI app, we load details from the rickle
.
We then get the settings for the endpoint "status". If the endpoint is not defined in the YAML, we simply don't create it.
That gives us the power to create a new YAML config for another country where the "status" endpoint does not exist.
Create from different things
The config does not have to be loaded from a YAML file. It does not even have to be loaded.
Or load it from a string of YAML/JSON/TOML etc.
Add templating-like string replacements
For the less likely event that you need to modify the input string dynamically before loading, arguments can be given as follows.
config_ZA.yaml | |
---|---|
And then the string will be searched and replaced before the YAML is loaded and a rickle
is constructed.
This will in effect change the YAML to the following (before loading it).
Even though the possibilities are opened up here, there are probably better ways to solve this (such as using ENV vars as shown later in this examples page).
Is this Jinja?
Noticable is that this is very similar to Jinja syntax, but it is is NOT Jinja.
Changing the handlebars
If you do use Jinja and want to still keep this sort of string replacement functionality, the handlebars can be changed:
Note
The length of the handlebar string MUST be an even number, i.e. 2, 4, 6 etc. This is because the string is chopped in the middle and the first half taken as opening braces, the second half as closing.
Loading multiple files
Rickle is not limited to only loading configs from one file/string. Multiple configs can be loaded into one rickle
at once.
# Load a list of YAML files
config = Rickle(['./config_US.yaml', './config_ZA.yaml'])
print(config[0].details.version)
print(config[1].details.version)
The behaviour here is that a list input results in the rickle
itself becoming a list object.
Changing _input_type
When a rickle
is created from a list object, the input type array
is assigned.
Multiple docs in YAML
rickle
can load a single YAML file containing multiple documents. For example:
Which, when loaded turn the rickle
into a list (array
) type.
JSONL (JSON lines)
As with multiple docs in YAML, JSON lines files can also be loaded using rickle
:
Which also turn the rickle
into a list (array
) type.
Referencing in YAML
What is especially powerful of YAML is the ability to add references. If we had a lot of duplication, we can simply reference the same values.
This results in the values on lines 18 and 24 are pre-filled with the value 'en-US'
as defined on line 12.
Similarly lines 19 and 25 are pre-filled with '1.1.0'
as defined on line 11.
Strings, Repr
A rickle
can have a string representation, which will be in YAML format.
>>> rick = Rickle('test.yaml')
>>> print(str(rick))
database:
host: 127.0.0.1
user: local
passw: ken-s3nt_me
>>> print(repr(r))
Rickle(database=Rickle(host='127.0.0.1', user='local', passw='ken-s3nt_me'))
Note
str
will give the deserialised version where repr
will give a raw (serialised) view.
Serialise to YAML, JSON, TOML, XML, INI.
A rickle
can also be dumped to YAML or JSON (or TOML, XML, INI).
By default, the dict
and to_yaml
(etc.) method returns the in deserialised form. For serialised form, serialised=True
can be passed.
The above example will give two different results based on serialisation:
>>> rick = Rickle('db_conf.yaml')
>>> print(rick.to_yaml())
root:
USERNAME: HE-MAN
HOST: 123.0.0.1
>>> print(rick.to_yaml(serialised=True))
root:
USERNAME:
type: env
load: USERNAME
HOST: 123.0.0.1
When rickle
is an array
When initialised as an array
(list-like) type, different behaviour for to_json
serialisation becomes available.
By default, if a rickle
is an array
, the to_json
method will serialise as a JSON lines string. This can be avoided by setting lines=False
:
>>> streams = Rickle('streams.jsonl') # Using streams.jsonl example
>>> print(streams.to_json())
{"name": "stream_alpha", "cap": 10}
{"name": "stream_fx", "cap": 1}
>>> print(streams.to_json(lines=False))
[{"name": "stream_alpha", "cap": 10}, {"name": "stream_fx", "cap": 1}]
dict
like functionality
A rickle
can act like a Python dictionary, like the following examples:
>>> rick = Rickle({'key' : 'value', 'k2' : 0.2})
>>> list(rick.items())
[('key', 'value'), ('k2', 0.2)]
>>> rick.values()
['value', 0.2]
>>> rick.keys()
['key', 'k2']
>>> rick.get('k2', default=0.42)
0.2
>>> rick.get('k3', default=0.42)
0.42
>>> rick['new'] = 0.99
>>> rick['new'] * rick['k2']
0.198
A rickle
can also be converted to a Python dictionary:
list
like functionality
Similarly, a rickle
can have list like functionality when it is an array
type.
Consider the "configs.yaml" example:
The list
method has to be used to serialise data:
>>> rickle = Rickle("configs.yaml")
>>> rickle.list()
[{'app': {'user': 'name', 'pass': 'admin'}}, {'app': {'user': 'bob', 'pass': 'god'}}]
>>> len(rickle)
2
>>> [rick.app.user for rick in rickle]
['name', 'bob']
Paths and searching
Another useful piece of functionality is the ability to use paths with rickle
.
Search keys
We can search for paths by using the search_path
method. Assume a rather large config file has multiple paths to settings concerning a "point":
>>> rickle.search_path('point')
['/config/default/point', '/config/control_A/control/point', '/config/control_B/control/point', '/docs/controls/point']
Finding key/value pairs
Where searching is only useful for finding a key path, when querying a key with a certain value, the find_key_value
method comes in handy:
And relative paths can be used to search in a more specific corner of the config:
Comparison operators
Comparison | Op | Alternative |
---|---|---|
equals | = |
eq |
not equals | != |
ne |
less than | < |
lt |
greater than | > |
gt |
less than equal | <= |
lte |
greater than equal | >= |
gte |
Using paths to get
, set
, put
, or remove
.
We can access the values by using the paths. If we have the following YAML:
example.yml | |
---|---|
Consider the following examples for using paths:
>>> rickle = Rickle("example.yml")
>>> rickle.get('/path/level_one/level_two/member') == 42
True
>>> rickle.get('/path/level_one/level_two/list_member')
[1,0,1,1,1]
>>> rickle.get('/path/level_one/level_two/list_member/[1]') == 0
True
>>> rickle.set('/path/level_one/level_two/member', 72)
>>> rickle.path.level_one.level_two.member == 72
True
>>> rickle.remove('/path/level_one/level_two/list_member')
>>> print(str(rickle))
path:
level_one:
level_two:
member: 72
Tip
The path separator can be specified by setting an environment variable "RICKLE_PATH_SEP", for example RICKLE_PATH_SEP=.
for dots, or using the init argument RICKLE_PATH_SEP
.