fields¶
- class cincoconfig.Field(*, key=None, schema=None, name=None, required=False, default=None, validator=None, sensitive=False, description=None, help=None, env=None)¶
The base configuration field. Fields provide validation and the mechanisms to retrieve and set values from a
Config
. Field’s are composable and reusable so they should not store state or store the field value.Validation errors should raise a
ValueError
exception with a brief message.There are three steps to validating a value:
validate()
- checks the value against the required parameter and then calls:_validate()
- validate function that is implemented in subclasses ofField
Field.validator
- custom validator method specified when the field is created
The pseudo code for the
validate()
function:1def validate(self, cfg, value): 2 if value is None and self.required: 3 raise ValueError 4 5 value = self._validate(cfg, value) 6 if self.validator: 7 value = self.validate(cfg, value) 8 return value
Since each function in the validation chain returns the value, each validator can transform the value. For example, the
BoolField
_validate
method converts the string value to abool
.Each Field has the following lifecycle:
__init__
- created by the application__setkey__()
- the field is added to aSchema
__setdefault__()
- the field is added to a config and the config is populated with thedefault value
Whenever a config value is set, the following methods are called in this order:
validate()
/_validate()
/validator
- validation chain__setval__()
- set a validated value to the config
Finally, whenever code retrieves a config value, the
__getval__()
is called.Most field subclasses only need to implement the
_validate
method and most do not need to implement the__setkey__
,__setdefault__
,__getval__
and__setval__
methods, unless the field needs to modify the default behavior of these methods.The Field
_key
is used to set and reference the value in the config.Each Field subclass can define a class or instance level
storage_type
which holds the annotation of the value being stored in memory.Environment Variables
Fields can load their default value from an environment variable. The Schema and Field accept an
env
argument in the constructor that controls whether and how environment variables are loaded. The default behavior is to not load any environment variables and to honor theField.default
value.There are two ways to load a field’s default value from an environment variable.
Schema.env
: ProvideTrue
or a string.Field.env
: ProvideTrue
or a string.
When
Schema.env
orField.env
isNone
(the default), the environment variable configuration is inherited from the parent schema. A value ofTrue
will load the the field’s default value from an autogenerated environment variable name, based on the field’s full path. For example:schema = Schema(env=True) schema.mode = ApplicationModeField(env="APP_MODE") schema.port = PortField(env=False) schema.db.host = HostnameField() schema.auth = Schema(env="SECRET") schema.auth.username = StringField()
The top-level schema is configured to autogenerate and load environment variables for all fields.
mode
is loaded from theAPP_MODE
environment variable.port
is not loaded from any the environment variable.db.host
is loaded from theDB_HOST
environment variable.The
auth
schema has a environment variable prefix ofSECRET
. All children and nested fields/schemas will start withSECRET_
.The
auth.username
field is loaded from theSECRET_USERNAME
environment variable.
All builtin Fields accept the following keyword parameters.
- Parameters
name (
Optional
[str
]) – field friendly name, used for error messages and documentationkey (
Optional
[str
]) – the key of the field in the config, this is typically not specified and, instead the__setkey__()
will be called by the configrequired (
bool
) – the field is required and aValueError
will be raised if the value isNone
default (
Union
[Callable
,Any
,None
]) – the default value, which can be a called that is invoke with no arguments and should return the default valuevalidator (
Optional
[Callable
[[Config
,Any
],Any
]]) – an additional validator function that is invoked during validationsensitive (
bool
) – the field stores a sensitive value
- _validate(cfg, value)¶
Subclass validation hook. The default implementation just returns
value
unchanged.- Return type
- __setkey__(schema, key)¶
Set the field’s _key, which is called when the field is added to a schema. The default implementation just sets
self._key = key
- __setdefault__(cfg)¶
Set the default value of the field in the config. This is called when the config is first created.
- __getval__(cfg)¶
Retrieve the value from the config. The default implementation retrieves the value from the config by the field key.
- __setval__(cfg, value)¶
Set the validated value in the config. The default implementation passes the value through the validation chain and then set’s the validated value int the config.
- property name¶
- Returns
the field’s friendly name:
name or key
- property short_help: Optional[str]¶
A short help description of the field. This is derived from the
help
attribute and is the first paragraph of text inhelp
. The intention is thatshort_help
can be used for the field description andhelp
will have the full documentation. For example:>>> field = Field(help=""" ... This is a short description ... that can span multiple lines. ... ... This is more information. ... """) >>> print(field.short_help) this is a short description that can span multiple lines.
- Returns
the first paragraph of
help
- to_basic(cfg, value)¶
Convert the Python value to the basic value.
The default implementation just returns
value
. This method is called when the config is saved to a file and will only be called with the value associated with this field.
- to_python(cfg, value)¶
Convert the basic value to a Python value. Basic values are serializable (ie. not complex types). The following must hold true for config file saving and loading to work:
assert field.to_python(field.to_basic(value)) == value
The default implementation just returns
value
. This method is called when the config is loaded from a file and will only be called with the value associated with this field.In general, basic types are any types that can be represented in JSON: string, number, list, dict, boolean.
- class cincoconfig.StringField(*, min_len=None, max_len=None, regex=None, choices=None, transform_case=None, transform_strip=None, **kwargs)¶
A string field.
The string field can perform transformations on the value prior to validating it if either transform_case or transform_strip are specified.
- Parameters
regex (
Optional
[str
]) – regex pattern that the value must matchtransform_case (
Optional
[str
]) – transform the value’s case to eitherupper
orlower
casetransform_strip (
Union
[bool
,str
,None
]) – strip the value by callingstr.strip()
. Setting this toTrue
will callstr.strip()
without any arguments (ie. striping all whitespace characters) and if this is astr
, thenstr.strip()
will be called withtransform_strip
.
- class cincoconfig.LogLevelField(levels=None, **kwargs)¶
A field representing the Python log level.
- class cincoconfig.ApplicationModeField(modes=None, create_helpers=True, **kwargs)¶
A field representing the application operating mode.
The create_helpers parameter will create a boolean
VirtualField
for eachmode
namedis_<mode>_mode
, that returnsTrue
when the mode is active. When create_helpers=True then each mode name must be a valid Python variable name.- Parameters
- class cincoconfig.SecureField(method='best', sensitive=True, **kwargs)¶
A secure storage field where the plaintext configuration value is encrypted on disk and decrypted in memory when the configuration file is loaded.
- Parameters
method (
str
) – encryption method, see_get_provider()
- class cincoconfig.IPv4AddressField(*, min_len=None, max_len=None, regex=None, choices=None, transform_case=None, transform_strip=None, **kwargs)¶
IPv4 address field.
The string field can perform transformations on the value prior to validating it if either transform_case or transform_strip are specified.
- Parameters
regex (
Optional
[str
]) – regex pattern that the value must matchtransform_case (
Optional
[str
]) – transform the value’s case to eitherupper
orlower
casetransform_strip (
Union
[bool
,str
,None
]) – strip the value by callingstr.strip()
. Setting this toTrue
will callstr.strip()
without any arguments (ie. striping all whitespace characters) and if this is astr
, thenstr.strip()
will be called withtransform_strip
.
- class cincoconfig.IPv4NetworkField(min_prefix_len=None, max_prefix_len=None, **kwargs)¶
IPv4 network field. This field accepts CIDR notation networks in the form of
A.B.C.D/Z
.- Parameters
- class cincoconfig.HostnameField(*, allow_ipv4=True, resolve=False, **kwargs)¶
A field representing a network hostname or, optionally, a network address.
- Parameters
allow_ipv4 (
bool
) – allow both a hostname and an IPv4 addressresolve (
bool
) – resolve hostnames to their IPv4 address and raise aValueError
if the resolution fails
- class cincoconfig.FilenameField(*, exists=None, startdir=None, **kwargs)¶
A field for representing a filename on disk.
The exists parameter can be set to one of the following values:
None
- don’t check file’s existenceFalse
- validate that the filename does not existTrue
- validate that the filename does exist"dir"
- validate that the filename is a directory that exists"file"
- validate that the filename is a file that exists
The startdir parameter, if specified, will resolve filenames starting from a directory and will cause all filenames to be validate to their absolute file path. If not specified, filename’s will be resolve relative to
os.getcwd()
and the relative file path will be validated.- Parameters
- class cincoconfig.BoolField(*, key=None, schema=None, name=None, required=False, default=None, validator=None, sensitive=False, description=None, help=None, env=None)¶
A boolean field.
All builtin Fields accept the following keyword parameters.
- Parameters
name (
Optional
[str
]) – field friendly name, used for error messages and documentationkey (
Optional
[str
]) – the key of the field in the config, this is typically not specified and, instead the__setkey__()
will be called by the configrequired (
bool
) – the field is required and aValueError
will be raised if the value isNone
default (
Union
[Callable
,Any
,None
]) – the default value, which can be a called that is invoke with no arguments and should return the default valuevalidator (
Optional
[Callable
[[Config
,Any
],Any
]]) – an additional validator function that is invoked during validationsensitive (
bool
) – the field stores a sensitive value
- FALSE_VALUES = ('f', 'false', '0', 'off', 'no', 'n')¶
Accepted values that evaluate to
False
- TRUE_VALUES = ('t', 'true', '1', 'on', 'yes', 'y')¶
Accepted values that evaluate to
True
- class cincoconfig.FeatureFlagField(*, key=None, schema=None, name=None, required=False, default=None, validator=None, sensitive=False, description=None, help=None, env=None)¶
Concrete implementation of the feature flag field. When this field’s value is set to
False
, the bound configurations will not perform validation.All builtin Fields accept the following keyword parameters.
- Parameters
name (
Optional
[str
]) – field friendly name, used for error messages and documentationkey (
Optional
[str
]) – the key of the field in the config, this is typically not specified and, instead the__setkey__()
will be called by the configrequired (
bool
) – the field is required and aValueError
will be raised if the value isNone
default (
Union
[Callable
,Any
,None
]) – the default value, which can be a called that is invoke with no arguments and should return the default valuevalidator (
Optional
[Callable
[[Config
,Any
],Any
]]) – an additional validator function that is invoked during validationsensitive (
bool
) – the field stores a sensitive value
- class cincoconfig.UrlField(*, min_len=None, max_len=None, regex=None, choices=None, transform_case=None, transform_strip=None, **kwargs)¶
A URL field. Values are validated that they are both a valid URL and contain a valid scheme.
The string field can perform transformations on the value prior to validating it if either transform_case or transform_strip are specified.
- Parameters
regex (
Optional
[str
]) – regex pattern that the value must matchtransform_case (
Optional
[str
]) – transform the value’s case to eitherupper
orlower
casetransform_strip (
Union
[bool
,str
,None
]) – strip the value by callingstr.strip()
. Setting this toTrue
will callstr.strip()
without any arguments (ie. striping all whitespace characters) and if this is astr
, thenstr.strip()
will be called withtransform_strip
.
- class cincoconfig.ListField(field=None, **kwargs)¶
A list field that can optionally validate items against a
Field
. If a field is specified, aListProxy
will be returned by the_validate
method, which handles individual item validation.Specifying required=True will cause the field validation to validate that the list is not
None
and is not empty.- Parameters
field (
Union
[BaseField
,Type
[ConfigType
],None
]) – Field to validate values against
- to_basic(cfg, value)¶
Convert to basic type.
- class cincoconfig.VirtualField(getter, setter=None, **kwargs)¶
A calculated, readonly field that is not read from or written to a configuration file.
- Parameters
getter (
Callable
[[Config
],Any
]) – a callable that is called whenever the value is retrieved, the callable will receive a single argument: the currentConfig
.setter (
Optional
[Callable
[[Config
,Any
],Any
]]) – a callable that is called whenever the value is set, the callable will receive two arguments:config, value
, the currentConfig
and the value being set
- class cincoconfig.DictField(key_field=None, value_field=None, **kwargs)¶
A generic
dict
field that optionally validates keys and values. Thekey_field
andvalue_field
parameters control whether and how the dictionary keys and values are validated, respectively. Setting these will cause the internal representation to be stored in aDictProxy
which handles the validation operations.Specifying required=True will cause the field validation to validate that the
dict
is notNone
and is not empty.- to_basic(cfg, value)¶
Convert to basic type.
- class cincoconfig.BytesField(encoding='base64', **kwargs)¶
Store binary data in an encoded string.
- ENCODINGS = ('base64', 'hex')¶
Available encodings: base64 and hex
- class cincoconfig.IncludeField(startdir=None, **kwargs)¶
A special field that can include another configuration file when loading from disk. Included files are in the same scope as where the include field is defined for example:
# file1.yaml db: include: "db.yaml" include: "core.yaml" # db.yaml host: "0.0.0.0" port: 27017 # core.yaml mode: "production" ssl: true
The final parsed configuration would be equivalent to:
db: host: "0.0.0.0" port: 27017 mode: "production" ssl: true
Included files must be in the same configuration file format as their parent file. So, if the base configuration file is stored in JSON then every included file must also be in JSON.
Cincoconfig does not track which configuration file set which field(s). When a config file is saved back to disk, it will be the entire configuration, even if it was originally defined across multiple included files.
- combine_trees(base, child)¶
An extension to
dict.update()
but properly handles nested dict objects.
- include(config, fmt, filename, base)¶
Include a configuration file and combine it with an already parsed basic value tree. Values defined in the included file will overwrite values in the base tree. Nested trees (
dict
objects) will be combined using adict.update()
like method,combine_trees()
.- Parameters
config (
Config
) – configuration objectfmt (
ConfigFormat
) – configuration file format that will parse the included filefilename (
str
) – included file pathbase (
dict
) – base config value tree
- Return type
- Returns
the new basic value tree containing the base tree and the included tree
Secure Fields¶
The following fields provide secure configuration option storage and challenges.
- class cincoconfig.ChallengeField(hash_algorithm='sha256', **kwargs)¶
A field whose value is securely stored as a hash (
DigestValue
). This field can be used as a secure method of password storage and comparison, since the password is only stored in hashed form and not in plaintext. A digest value is pair of salt andhash(salt + plaintext)
values.Values are stored in memory as
DigestValue
instances. For example:>>> schema = Schema() >>> schema.password = ChallengeField('md5') >>> cfg = schema() >>> cfg.password = "Hello" >>> print(type(cfg.password)) <class 'cincoconfig.fields.DigestValue'> >>> print(cfg.password) Yt4Qm5cC9FoRSdU3Ly7B7A==:+GXXhO36XvJ446fqXYJ+1w== >>> cfg.password.digest b'øe×íú^òxã§ê]~×'
The
default
value of a challenge field can be either:A plaintext string. In this case, the salt will be randomly generated.
A
DigestValue
instance.
When the default value is a string, the salt will change between application executions. For example:
>>> schema = Schema() >>> schema.password = ChallengeField('md5', default='hello') >>> cfg = schema() # First time application executes >>> print(cfg.password) Yt4Qm5cC9FoRSdU3Ly7B7A==:+GXXhO36XvJ446fqXYJ+1w== # Second time application executes >>> print(cfg.password) c2MPwSJw1QYMOcE2O+cVFA==:JSNbBj3wCgh7alFM7l0geg==
Digest values are saved to disk as a
dict
containing two keys:salt
- base64 encoded saltdigest
- base64 encoded digest
The challenge field supports loading plaintext string values from the configuration file. So, when manually writing the config file, the user does not need to create the salt and digest pair but, instead, just specify a plaintext string to hash. The value will be properly saved as a salt/digest pair the next time the config file is saved to disk.
Available hash algorithms are:
md5
sha1
sha224
sha256
sha384
sha512
- Parameters
hash_algorithm (
str
) – hash algorithm to use, must be a key ofALGORITHMS
-
ALGORITHMS:
Dict
[str
,Callable
[[bytes
], hashlib._Hash]] = {'md5': <built-in function openssl_md5>, 'sha1': <built-in function openssl_sha1>, 'sha224': <built-in function openssl_sha224>, 'sha256': <built-in function openssl_sha256>, 'sha384': <built-in function openssl_sha384>, 'sha512': <built-in function openssl_sha512>}¶ Available hashing algorithms
- storage_type¶
alias of
DigestValue
- to_basic(cfg, value)¶
Convert to a dict and indicate the type so we know on load whether we’ve already dealt with the field
- Parameters
cfg (
Config
) – current configvalue (
DigestValue
) – value to encrypt/hash
- Return type
- Returns
encrypted/hashed value
- to_python(cfg, value)¶
Decrypt the value if loading something we’ve already handled. Hash the value if it hasn’t been hashed yet.
- Parameters
- Return type
- Returns
decrypted value or unmodified hash
- Raises
ValueError – if the value read from the config is neither a dict nor a string
- class cincoconfig.DigestValue(salt, digest, algorithm)¶
Digest value tuple storing hashed value: (salt, digest, algorithm). The digest is the hash of the concatenated salt and plaintext value (
hash(salt + plaintext)
).Create new instance of TDigestValue(salt, digest, algorithm)
- challenge(plaintext)¶
Challenge a plaintext value against the digest value. This will raise a
ValueError
if the challenge is unsuccessful.- Raises
ValueError – the challenge was unsuccessful
- Return type
- classmethod create(plaintext, algorithm, salt=None)¶
Hash a plaintext value and return the new digest value. The digest is calculated as:
salt[:digest_size] + plaintext
The salt will be randomly generated if not specified. If the salt is specified and it is larger than the algorithm
digest_size
, the salt will be truncated to thedigest_size
.- Parameters
plaintext – string to hash
algorithm – hashlib algorithm to use
salt – hash salt
- Returns
the created digest value
- classmethod parse(value, algorithm)¶
Parse a base64-encoded salt/digest pair, as returned by
__str__()
- class cincoconfig.SecureField(method='best', sensitive=True, **kwargs)¶
A secure storage field where the plaintext configuration value is encrypted on disk and decrypted in memory when the configuration file is loaded.
- Parameters
method (
str
) – encryption method, see_get_provider()
Instance Method Field¶
- cincoconfig.instance_method(schema, name)¶
Bind a function to a schema as an instance method. Use this as a decorator:
schema = Schema() @instance_method(schema, "say_hello") def say_hello_method(config: Config) -> str: return "Hello, world!" config = schema() print(config.say_hello()) # "Hello, world!"
Internal Types and Base fields¶
The following classes are used internally by cincoconfig and should not have to be used or
referenced directly in applications. These are not included in the public API and must be imported
explicitly from the cincoconfig.fields
module.
- class cincoconfig.DictProxy(cfg, dict_field, iterable=None)¶
A Field-validated
dict
proxy. This proxy supports all methods that the builtindict
supports with the added ability to validate keys and values against aField
. This is the value returned by theDictField
validation chain.
- class cincoconfig.ListProxy(cfg, list_field, iterable=None)¶
A Field-validated
list
proxy. This proxy supports all methods that the builtinlist
supports with the added ability to validate items against aField
. This is the field returned by theListField
validation chain.
- class cincoconfig.NumberField(type_cls, *, min=None, max=None, **kwargs)¶
Base class for all number fields. This field should not be used directly, instead consider using
IntField
orFloatField
.