Configuration schema validation

As noted in index, your configuration is mostly supposed to be a dict. To validate your schema, you should instantiate a Descriptor. Descriptor reflects how your config is nested.

class satella.configuration.schema.Boolean

This value must be a boolean, or be converted to one

class satella.configuration.schema.Float

This value must be a float, or be converted to one

BASIC_MAKER

alias of float

class satella.configuration.schema.Integer

This value must be an integer, or be converted to one

BASIC_MAKER

alias of int

class satella.configuration.schema.String

This value must be a string, or be converted to one

BASIC_MAKER

alias of str

class satella.configuration.schema.File

This value must be a valid path to a file. The value in your schema will be an instance of FileObject

class satella.configuration.schema.FileObject(path)

What you get for values in schema of File.

This object is comparable and hashable, and is equal to the string of it’s path

Parameters:

path (str) –

get_value(encoding=None)

Read in the entire file into memory

Parameters:

encoding (Optional[str]) – optional encoding to apply. If None given, bytes will be returned

Returns:

file contents

Return type:

Union[str, bytes]

open(mode)

Open the file in specified mode

Parameters:

mode (str) – mode to open the file in

Returns:

file handle

class satella.configuration.schema.FileContents(encoding=None, strip_afterwards=False)

This value must be a valid path to a file. The value in your schema will be the contents of this file, applied with encoding (if given). By default, bytes will be read in

Parameters:
  • encoding (Optional[str]) –

  • strip_afterwards (bool) –

class satella.configuration.schema.Directory

This value must be a valid path to a file. The value in your schema will be an instance of FileObject

class satella.configuration.schema.DirectoryObject(path)

What you get for values in schema of Directory.

This object is comparable and hashable, and is equal to the string of it’s path

Parameters:

path (str) –

get_files()

Return a list of files inside this directory :return:

Return type:

Iterable[str]

class satella.configuration.schema.basic.FileObject(path)

What you get for values in schema of File.

This object is comparable and hashable, and is equal to the string of it’s path

Parameters:

path (str) –

get_value(encoding=None)

Read in the entire file into memory

Parameters:

encoding (Optional[str]) – optional encoding to apply. If None given, bytes will be returned

Returns:

file contents

Return type:

Union[str, bytes]

open(mode)

Open the file in specified mode

Parameters:

mode (str) – mode to open the file in

Returns:

file handle

class satella.configuration.schema.IPv4

This must be a valid IPv4 address (no hostnames allowed)

class satella.configuration.schema.List(type_descriptor=None)

This must be a list, made of entries of a descriptor (this is optional)

Parameters:

type_descriptor (Optional[Descriptor]) –

BASIC_MAKER

alias of list

class satella.configuration.schema.Dict(keys, unknown_key_mapper=<function Dict.<lambda>>)

This entry must be a dict, having at least specified keys.

Use like:

>>> Dict([
>>>     create_key(String(), 'key_s'),
>>>     create_key(Integer(), 'key_i'),
>>>     create_key(Float(), 'key_f'),
>>>     create_key(String(), 'key_not_present', optional=True,
>>>                default='hello world'),
>>>     create_key(IPv4(), 'ip_addr')
>>>])
Parameters:
  • keys (List[DictDescriptorKey]) –

  • unknown_key_mapper (Callable[[str, Optional[Union[int, float, str, dict, list, bool]]], Any]) –

BASIC_MAKER

alias of dict

class satella.configuration.schema.Caster(to_cast)

A value must be ran through a function.

Use like:

>>> class Environment(enum.IntEnum):
>>>     PRODUCTION = 0
>>> assert Caster(Environment)(0) == Environment.PRODUCTION
Parameters:

to_cast (Callable[[Any], Any]) –

Then there is a descriptor that makes it possible for a value to have one of two types:

class satella.configuration.schema.Union(*descriptors)

The type of one of the child descriptors. If posed as such:

Union(List(), Dict())

then value can be either a list or a dict

Parameters:

descriptors (List[Descriptor]) –

You can use the following to declare your own descriptors:

class satella.configuration.schema.Descriptor

Base class for a descriptor

class satella.configuration.schema.Regexp

Base class for declaring regexp-based descriptors. Overload it’s attribute REGEXP. Use as following:

>>> class IPv6(Regexp):
>>>     REGEXP = '(([0-9a-f]{1,4}:)' ...

Just remember to decorate them with

satella.configuration.schema.register_custom_descriptor(name, is_plain=True)

A decorator used for registering custom descriptors in order to be loadable via descriptor_from_dict

Use like:

>>> @register_custom_descriptor('ipv6')
>>> class IPv6(Regexp):
>>>     REGEXP = '(([0-9a-f]{1,4}:)' ...
Parameters:
  • name (str) – under which it is supposed to be invokable

  • is_plain (bool) – is this a nested structure?

If you want them loadable by the JSON-schema loader.

You use the descriptors by calling them on respective values, eg.

>>> List(Integer())(['1', '2', 3.0])
[1, 2, 3]

JSON schema

The JSON schema is pretty straightforward. Assuming the top-level is a dict, it contains keys. A key name is the name of the corresponding key, and value can have two types. Either it is a string, which is a short-hand for a descriptor, or a dict containing following values:

{
    "type": "string_type",
    "optional": True/False,
    "default": "default_value" - providing this implies optional=True
}

Note that providing a short-hand, string type is impossible for descriptors that take required arguments.

Available string types are:

You can use file contents as follows:

{
    "contents": {
        "type": "file_contents",
        "encoding": "utf-8
    }
}

Or just

{
    "contents": "file_contents"
}

But in this case, bytes will be read in.

Lists you define as following

{
    "type": "list",
    "of": {
        ".. descriptor type that this list has to have .."
    }
}

Unions you define the following

{
    "type": "union",
    "of": [
        ".. descriptor type 1 ..",
        ".. descriptor type 2 .."
        ]
}

Dicts are more simple. Each key contains the key that should be present in the dict, and value is it’s descriptor - again, either in a short form (if applicable) or a long one (dict with type key).

You load it using the following function:

satella.configuration.schema.descriptor_from_dict(dct)

Giving a Python dictionary-defined schema of the configuration, return a Descriptor-based one

Parameters:

dct (dict) – something like

Return type:

Descriptor

{

“a”: “int”, “b”: “str”, “c”: {

“type”: “int” “optional”: True, “default”: 5

}, “d”: {

“a”: “int”, “b”: “str”

}

}

although you can pass “int”, “float” and “str” without enclosing quotes, that will work too

Returns:

a Descriptor-based schema

Parameters:

dct (dict) –

Return type:

Descriptor

Casters you define as

{
    "type": "caster"
    "cast_to": "name of a built-in or a fully qualified class ID"
}

If cast_to is not a builtin, it specifies a full path to a class, which will be loaded using satella.imports.import_class().

Additionally, an extra argument can be specified:

{
    "type": "caster",
    "cast_to": "name of a built-in or a FQ class ID",
    "expr": "y(int(x))"
}

In which case cast_to will be displayed as a y in expression, which will be eval()ed, and this value will be output. The input value will be called x.

You can also provide a commentary for your entries:

{
    "contents": {
        "type": "file_contents",
        "encoding": "utf-8,
        "description": "Encryption key (private key)",
        "strip_afterwards": True
    },
    "max_workers": {
        "type": "int",
        "description": "Maximum parallel instances of service"
    }
}

strip_afterwards (default is False) strips the content of loaded file of trailing and leading whitespace.