API

Bonfig

An alternative, more beautiful way to build configs!

class bonfig.Bonfig(*args, frozen=True, **kwargs)

Base class for all Bonfigs.

Parameters:
frozen : bool, optional

Freeze Bonfig just after initialisation - by calling Bonfig.freeze().

*args

Positional arguments, passed to Bonfig.load().

**kwargs

Keyword arguments, passed to Bonfig.load().

Notes

Instances of this class shouldn’t be created directly, instead it should be subclassed (see examples).

Ideally do overwrite or call __init__ directly, instead use the provided load hook for initialising things like data stores.

Attributes:
__fields__ : set

a set containing all the classes Field attributes.

__store_attrs__ : set

a set containing the names of each store attribute for that class

Methods

freeze() Freeze Bonfig stores.
load(*args, **kwargs) Hook called during initialisation for loading store attributes.
freeze()

Freeze Bonfig stores.

Works by creating a copy of each store as dict, then converting to an MappingProxyType.

Notes

In order to ‘freeze’ your store, it each container needs to implement both __getitem__() and keys() as a minimum.

load(*args, **kwargs)

Hook called during initialisation for loading store attributes.

Is basically __init__, but called at the right time such that locking works and that fields can be initialised.

Parameters:
*args

args from __init__

**kwargs

kwargs from __init__

Notes

As load is called before Field`s are initialised, values can be overwritten by fields unless `Field.val=None.

class bonfig.Store(_name=None)

Placeholder class, allowing structure of store to be created in Bonfig.

Upon initialisation should be overwritten with an container object that supports __getitem__ using Bonfig.load().

Any Fields ultimately belonging to a Store will look in the container that the Store object is overwritten with during Bonfig.load().

Examples

>>> class Config(Bonfig):
...     a = Store('my store')
...     b = Store()
...
...     f1 = a.Field()
...     f2 = b.Field()
>>> Config.a.name
'my store'
>>> Config.b.name
'b'
>>> Config.a.Field
functools.partial(<class 'bonfig.fields.Field'>, _store=<Store: my store>)
>>> Config.b.Section
functools.partial(<class 'bonfig.fields.Section'>, _store=<Store: b>)
Attributes:
name : str, optional

Name of store.

is_with_proxy

Check if self is an original Store or a proxy of another from a with statement.

name

Name of store.

For any Fields that ultimately belong to this store will have this value as Field.store_attr.

Fields

NOTE

You will notice in the Field and Section parameters for __init__ which start with ‘_’, these arguments are not intended to be set directly. Instead these classes should be accessed by either parent Stores or Sections, and these arguments will be implicitly set.

Attributes

fields : FieldDict
A global dict -like object that stores all of the field classes. This dict is used to provide stores and sections with access to field classes, as such, add to this global dict to make custom fields available in that context. (By default includes Field, IntField, FloatField, BoolField and DatetimeField.
class bonfig.fields.Section(name=None, *, _supsection=None, _store=None)

Convenience class for building up multi-level Bonfigs s.

Any Field s created from Section.Field implicitly have the Field.section attribute set to that Section.

Parameters:
name : str, optional

name of Section. Default behaviour is to take the name of the class attribute Section was set to (using __set_name__).

_supsection : Section, optional

Section that owns this section. Rather than setting directly, this should be done by creating this Section instance using supsection.Section.

_store : Store

Store that section belongs to.

Examples

>>> class Config(Bonfig):
...     s = Store()
...
...     A = s.Section()
...     Af = A.Field('A - f')
...
...     B = A.Section('B within A')
...     Bf = B.Field('B - f')
...
...     def load(self):
...         self.s = {}
>>>
>>> c = Config()
>>> c.A.keys
['sec_A']
>>> c.B.keys
['A', 'B within A']
>>> c.Af
'A - f'
>>> Config.Af.keys
['A', 'Af']
>>> Config.Bf.keys
['A', 'B within A', 'Bf']
>>> c.s
{'A':
{
'Af': 'A - f',
'B within A':
    {
    'Bf': 'B - f'
    }
}
}
Attributes:
is_with_proxy

Check if self is an original Section or a proxy of another from a with statement.

keys

Keys used to look get to this section of the store

name

Name of Section.

classmethod _from_with(with_owner)

Internal method for creating a ‘proxy’ of a Section object for use in with blocks.

is_with_proxy

Check if self is an original Section or a proxy of another from a with statement.

keys

Keys used to look get to this section of the store

name

Name of Section.

class bonfig.fields.Field(val=None, default=None, name=None, *, _store=None, _section=None)

Class representing a parameter from your configuration. This object encapsulates how this parameter is serialised and deserialsed as well as how and where it’s fetched from.

Parameters:
val : object, optional

value that will be inserted into store upon initialisation. (Note if set to None, no value will be added to the store rather that a value equal to None.)

default : object, optional

Fallback value for this parameter if not found in store. If set to None then AttributeError will be raised.

name : str, optional

Key that is used to get value of Field as stored in store. Default behaviour is to take the name of the class attribute Field was set to (using __set_name__).

_store : Store

store that this Field belongs to/ looks into. Shouldn’t really be set directly, instead use Store.Field to create Field instances.

_section : Section, optional

section that this Field belongs to. The section provides a set of ‘pre-keys’ that are looked up before name in store in order to fetch the Field ‘s value. Rather than setting directly, Section.Field should be used.

Notes

The __add__ method of Field objects is also implemented for convenience, however, note that as the Store and Section of a field is derived from the Store or Section that ultimately made it, this could lead to unexpected behaviour if this is used between stores and sections e.g.

Works:

>>> from bonfig import Bonfig, Store
>>> class Config(Bonfig):
...     s = Store()
...     FIRST_NAME = s.Field("Garry")
...     FULL_NAME = FIRST_NAME + 'Lineker'
...
>>> c = Config()
>>> c.FIRST_NAME
"Garry"
>>> c.FULL_NAME
"GarryLineker"

Also works:

>>> from bonfig import Bonfig, Store
>>> class Config(Bonfig):
...     s = Store()
...     FIRST_NAME = s.Field("Garry")
...     LAST_NAME = s.Field("Lineker")
...     FULL_NAME = FIRST_NAME + LAST_NAME
...
>>> c = Config()
>>> c.FULL_NAME
"GarryLineker"

Examples

>>> class Config(Bonfig):
...     s = Store()
...
...     f1 = s.Field('first field')
...     f2 = s.Field()
...     f3 = s.Field(default='f3 not found')
...     f4 = s.Field('fourth field', name='field1')
...
...     def load(self):
...         self.s = {}
>>> c = Config()
>>> c.f1
'first field'
>>> c.f2
KeyError: 'f2'
>>> c.f3
'f3 not found'
>>> c.f4
'forth field'
>>> c.s
{'f1': 'first field', 'field1': 'fourth field'}
Attributes:
keys

Keys used to look up value within store.

store_attr

Name of Bonfig instance attribute that values are looked up in.

_get_store(bonfig)

Get store attribute from bonfig.

_get_value(store)

Hook to allow you to control how the value is looked up in the data Store.

_initialise(bonfig)

Initialise Field.

This method is called during initialisation, and sets the value found within store at self.keys to self.val, unless self.val is None, in which case, nothing happens!

Notes

This process takes place just after Bonfig.load() and for each Field. As such any values set from Bonfig.load are liable to be overwritten by initialise unless self.val=None.

_post_get(val)

Apply function to values after they are fetched from data Store

_pre_set(val)

Apply function to values before they are inserted into data Store

keys

Keys used to look up value within store.

store_attr

Name of Bonfig instance attribute that values are looked up in.

bonfig.fields.make_sub_field(name, bases, post_get=None, pre_set=None, doc=None)

Factory function for producing Field-like classes.

Parameters:
name : str

Name of new field class

post_get : func

Function to apply to values just after they are fetched from the data Store.

pre_set : func

Function to apply to values just before they are inserted into the data Store.

bases : cls

Class for the newly created class to subclass (defaults to Field).

doc : str, optional

docstring :)

Returns:
cls : cls

New Field subclass.

class bonfig.fields.FieldDict(bases, *args, **kwargs)

Subclass of dict for storing field classes.

Methods

add(field_cls) Add a field_cls class to a field_cls dict.
clear()
copy()
fromkeys($type, iterable[, value]) Create a new dictionary with keys from iterable and values set to value.
get($self, key[, default]) Return the value for key if key is in the dictionary, else default.
items()
keys()
make_quick(name[, post_get, pre_set, …]) Factory function for producing Field -like classes.
pop(k[,d]) If key is not found, d is returned if given, otherwise KeyError is raised
popitem() 2-tuple; but raise KeyError if D is empty.
setdefault($self, key[, default]) Insert key with a value of default if key is not in the dictionary.
update([E, ]**F) If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
values()
add(field_cls)

Add a field_cls class to a field_cls dict. This method implicitly uses field_cls.__name__ as the key.

Parameters:
field_cls : cls

A Field subclass that you want to be added to the dict

Returns:
field_cls : cls

Returns the same field_cls instance back. This allows the method to be used as a class decorator!

Examples

>>> @fields.add
>>> class ListField(Field):
...
...     def __init__(self, val=None, default=None, name=None, *, _store=None, _section=None, sep=', '):
...         super().__init__(val, default=default, name=name, _store=_store, _section=_section)
...         self.sep = sep
...
...         def post_get(self, val):
...             return val.split(self.sep)
...
...         def pre_set(self, val):
...             return self.sep.join(val)
make_quick(name, post_get=None, pre_set=None, doc_preamble=None)

Factory function for producing Field -like classes.

Parameters:
name : str

Name of new class

post_get : func, optional

Function to apply to values just after they are fetched from the data Store.

pre_set : func, optional

Function to apply to values just before they are inserted into the data Store.

bases : cls

Class for the newly created class to subclass (defaults to Field).

doc_preamble : str, optional

docstring to prepend to Field docstring!

Returns:
cls : cls

New Field subclass.

Examples

>>> IntField = fields.make_quick('IntField', int, str)
class bonfig.fields.IntField(val=None, default=None, name=None, *, _store=None, _section=None)

Field that serialises and de-serialises values as integers.

Values are stored as strings within store

Parameters:
val, default, name, fmt, _store, _section : object

See baseclass Field

See also

Field
Parent class

Examples

>>> class Config(Bonfig):
...     s = Store()
...     days = s.IntField(365)
...
>>> c = Config()
>>> c.days
365
>>> c.s
{'days': '365'}
Attributes:
keys

Keys used to look up value within store.

store_attr

Name of Bonfig instance attribute that values are looked up in.

_post_get(val)

Apply function to values after they are fetched from data Store

_pre_set(val)

Apply function to values before they are inserted into data Store

class bonfig.fields.BoolField(val=None, default=None, name=None, *, _store=None, _section=None)

Field that serialises and de-serialises values as Booleans.

Values are stored as strings within store

Parameters:
val, default, name, fmt, _store, _section : object

See baseclass Field

See also

Field
Parent class

Examples

>>> class Config(Bonfig):
...     s = Store()
...     do = s.BoolField(True)
...
>>> c = Config()
>>> c.do
True
>>> c.s
{'do': 'True'}
Attributes:
keys

Keys used to look up value within store.

store_attr

Name of Bonfig instance attribute that values are looked up in.

_post_get(val)

Apply function to values after they are fetched from data Store

_pre_set(val)

Apply function to values before they are inserted into data Store

class bonfig.fields.FloatField(val=None, default=None, name=None, *, _store=None, _section=None)

Field that serialises and de-serialises values as floats.

Values are stored as strings within store

Parameters:
val, default, name, fmt, _store, _section : object

See baseclass Field

See also

Field
Parent class

Examples

>>> class Config(Bonfig):
...     s = Store()
...     pi = s.FloatField(3.14159)
...
>>> c = Config()
>>> c.pi
3.14159
>>> c.s
{'pi': '3.14159'}
Attributes:
keys

Keys used to look up value within store.

store_attr

Name of Bonfig instance attribute that values are looked up in.

_post_get(val)

Apply function to values after they are fetched from data Store

_pre_set(val)

Apply function to values before they are inserted into data Store

class bonfig.fields.DatetimeField(val=None, default=None, name=None, fmt=None, *, _store=None, _section=None)

Field that serialises and de-serialises fields that are datetime strings!

Values are stored as strings within store

Parameters:
val : str, datetime.datetime

Either str or datetime, if str will be automatically converted to datetime.

fmt : str

Required keyword argument - format to read and write datetime with (using datetime.datetime.strptime and strftime)

default, name, _store, _section : object

See Field

See also

Field
Parent class

Examples

>>> class Config(Bonfig):
...     s = Store()
...     when = s.DatetimeField('25/12/1995', fmt='%d/%m/%Y')
...
>>> c = Config()
>>> c.when
datetime.datetime(1995, 12, 25, 0, 0)
>>> c.s
{'when': '25/12/1995'}
Attributes:
keys

Keys used to look up value within store.

store_attr

Name of Bonfig instance attribute that values are looked up in.

_post_get(val)

Apply function to values after they are fetched from data Store

_pre_set(val)

Apply function to values before they are inserted into data Store

class bonfig.fields.PathField(val=None, default=None, name=None, *, _store=None, _section=None)

Field that serialises and de-serialises fields that are pathlib.Path objects.

Values are stored as strings within store.

Parameters:
val : str, pathlib.Path

Either str or pathlib.Path, if str, will be automatically converted to pathlib.Path.

default, name, fmt, _store, _section : object

See Field

See also

Field
Parent class

Examples

>>> class Config(Bonfig):
...     s = Store()
...     base = s.PathField('')  # '' required as None is not a valid Path
...     file = base / 'important.txt'
...
>>> c = Config()
>>> c.base
Path('.')
>>> c.file
Path('important.txt')
>>> c.s
Attributes:
keys

Keys used to look up value within store.

store_attr

Name of Bonfig instance attribute that values are looked up in.

_post_get(val)

Apply function to values after they are fetched from data Store

_pre_set(val)

Apply function to values before they are inserted into data Store