Examples¶
Contents
Configurations interaction¶
Configuration could utilize previously produced objects:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | """
Example of cross-linked configurations.
There are two sections 'x' and 'y':
* 'x' has multiple configurations 'x__1', 'x__2'.
'x__1' remains `init` unchanged, 'x__2' patches default `pycnfg.Producer` to
add 'replace' method and apply it on `init`.
* 'y' has single 'y__conf'.
'y__conf' uses `CustomProducer` and awaits for 'x__1'/'x__2' object in steps,
either by value, or by identifier. `CustomProducer`.__init__() awaits for
'logger_id' and 'path_id'.
Internally ``pycnfg.run``:
* Merging user CNFG with default ``pycnfg.CNFG``, so additional sections
path and logger will be added.
* Executes available configurations in ``priority`` and add resulted objects to
`objects` storage:
* produces 'path__default'.
* produces 'logger__default'.
* produces 'x__1', 'x__2'.
* produces 'y__conf' using previously created objects.
"""
import pycnfg
class CustomProducer(pycnfg.Producer):
"""Specify methods to produce object."""
def __init__(self, objects, oid, path_id, logger_id):
pycnfg.Producer.__init__(self, objects, oid)
self.logger = objects[logger_id]
self.project_path = objects[path_id]
def set(self, obj, key, val):
obj[key] = val
return obj
def log(self, obj, key=None, key_id=None):
if key is None:
# Extract from cross-configurations storage.
key = self.objects[key_id]
self.logger.info(f'{obj[key]}')
return obj
def func(self, obj, key):
"""Replace obj."""
obj = key
return obj
# Configuration.
CNFG = {
'x': {
'1': {
'init': 'a',
'producer': pycnfg.Producer,
'steps': [],
'priority': 1,
},
'2': {
'init': 'b',
'producer': pycnfg.Producer,
'patch': {'replace': func},
'steps': [
('replace', {'key': 'c'}),
],
'priority': 1,
}
},
'y': {
'conf': {
'init': {'b': 2, 'c': 42},
'producer': CustomProducer,
'steps': [
('__init__', {'path_id': 'path__default',
'logger_id': 'logger__default'}),
('set', {'key': 'x__1', 'val': 7}),
('log', {'key': 'x__2'}),
('log', {'key_id': 'x__2'}),
],
'priority': 2,
}
},
}
if __name__ == '__main__':
# Execute configuration(s).
objects = pycnfg.run(CNFG)
# => 42
# => 42
# Storage for produced object(s).
print(objects)
# => {'logger__default': <Logger default (INFO)>,
# 'path__default': 'pycnfg/examples/complex',
# 'x__1': 'a',
# 'x__2': 'c',
# 'y__conf': {'b': 2, 'c': 42, 'a': 7}}
|
Flexibility¶
Pycnfg provides flexibility in configuration destiption:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """
Example of configuration syntax.
There are multiple CNFG provided equivalent result:
* set key 'b' with 42.
* log.
"""
import pycnfg
class CustomProducer(pycnfg.Producer):
"""Specify methods to produce object."""
def __init__(self, objects, oid):
# Mandatory.
super().__init__(objects, oid)
def set(self, obj, key, val=42):
obj[key] = val
return obj
def print(self, obj, key='a'):
print(obj[key])
return obj
# Original.
CNFG_1 = {
'section_id': {
'configuration_id': {
'init': {'a': 7},
'producer': CustomProducer,
'steps': [
('set', {'key': 'b', 'val': 42}),
('print', {'key': 'b'}),
],
}
}
}
# Use CNFG level 'global' to rewrite 'key' from 'c' to 'b' in 'set'/'print'.
# Use section level 'global' to rewrite 'val' from '24' on '42'.
# NOTE: In pycnfg.run() should be set update_expl=True to allow replace
# explicitly set kwargs with global values (by default False).
CNFG_2 = {
'global': {
'key': 'b',
'print__key': 'b', # Targeted alternative (higher priority).
},
'init': {'a': 7},
'section_id': {
'global': {'val': 42},
'configuration_id': {
'producer': CustomProducer,
'steps': [
('set', {'key': 'c', 'val': 24}),
('print', {'key': 'c'}),
],
}
}
}
# Resolve None via separate sections.
# Sections could be reused multiple times.
# NOTE: In pycnfg.run() should be set resolve_none=True to allow resolve
# explicitly set to None kwargs via sub-configuration (by default False).
CNFG_3 = {
'section_id': {
'configuration_id': {
'init': {'a': 7},
'producer': CustomProducer,
'priority': 2,
'steps': [
('set', {'key': None, 'val': None}),
('print', {'key': None}),
],
}
},
'key': {
'conf': {
'init': 'b',
}
},
'val': {
'conf': {
'init': 42,
}
},
}
# Resolve values via separate section id.
CNFG_4 = {
'section_id': {
'configuration_id': {
'init': {'a': 7},
'producer': CustomProducer,
'priority': 2,
'steps': [
('set', {'key': 'key__conf', 'val': 'val__conf2'}),
('print', {'key': 'key__conf'}),
],
}
},
'key': {
'conf': {
'init': 'b',
},
},
'val': {
'conf': {
'init': 24,
},
'conf2': {
'init': '42',
}
},
}
if __name__ == '__main__':
for cnfg in [CNFG_1, CNFG_2, CNFG_3, CNFG_4]:
# Execute configuration(s).
objects = pycnfg.run(cnfg, dcnfg={}, update_expl=True, resolve_none=True)
# => 42
# Storage for produced object(s).
print(objects['section_id__configuration_id'])
# => {'a': 7, 'b': 42}
print('=' * 79)
|