Automatic Instantiation

Beyond accessing primitives or simple containers like lists and dicts, you can also create arbitrarily complex components automatically with the config object.

All you need is to include the key _type which specifies the name of the component that should be instantiated. The config object will then automatically instantiate the component and pass the config node to the component’s constructor (unless the component is configurable).

See the feature slide B7.

Here’s an example:

import omnifig as fig

@fig.component('cmp1')
class Component1():
    def __init__(self, config):
        self.x = config.pull('x') # will raise an error if 'x' is not found
        self.y = config.pull('y', 10)

@fig.component('cmp2')
class Component2(fig.Configurable):
    def __init__(self, x, y=20):
        self.x = x
        self.y = y

cfg = fig.create_config(
          b1={'_type': 'cmp1', 'x': 'value'},
          b2={'_type': 'cmp2', 'x': 'value2', 'y': 1},
)

print(cfg) # prints out:
# obj1:
#   _type: cmp1
#   x: value
# obj2:
#   _type: cmp2
#   x: value2
#   y: 1

obj1 = cfg.pull('b1') # instantiates a Component1 object

assert obj1.x == 'value'
assert obj1.y == 10
assert isinstance(obj1, Component1)

obj2 = cfg.pull('b2') # instantiates a Component2 object

assert obj2.x == 'value2'
assert obj2.y == 1
assert isinstance(obj2, Component2)

Additionally, you can specify modifiers with the _mod key, and a custom creator with the``_creator`` key for more control over what gets instantiated and how.

Creating/Processing Values

By default, only one instance of each component is created, so when pulling repeatedly, you will get the same instance of the component. However, this behavior can be adjusted by changing the settings or by creating instances explicitly using create. In contrast, you can explicitly access the value of a node including passing in positional and keyword arguments using process. For convenience, there are a few methods that combine the two: peek_create, peek_process (see for more info about peeking). Lastly, you can clear all instantiated components for the whole config tree using clear_product.

Continuing the example above:

obj3 = cfg.pull('b1') # returns the same object as obj1

assert obj1 is obj3

assert obj2 is cfg.peek_process('b2') # returns the same object as obj2

obj4 = cfg.peek_create('b2') # returns a new object

assert obj2 is not obj4

cfg.clear_product() # clears all instantiated components

assert obj1 is not cfg.pull('b1') # returns a new object

cfg2 = fig.create_config(_type='cmp2', x='value3', y=0)

obj5 = cfg2.process(y=100)

assert obj5.x == 'value3'
assert obj5.y == 100

obj6 = cfg2.process(y=200)

assert obj6.x == 'value3'
assert obj6.y == 100 # not 200 because the component was already instantiated
assert obj5 is obj6

obj7 = cfg2.create('value4', y=200)

assert obj7.x == 'value4'
assert obj7.y == 200 # because the component was created explicitly
assert obj7 is not obj6