Examples

Configurations interaction

Configuration could utilize previously produced objects:

github repo

  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:

github repo

  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)