bliss_controller.py 3.96 KB
Newer Older
Perceval Guillou's avatar
Perceval Guillou committed
1
2
3
4
5
6
7
# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) 2015-2020 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.

8
from unittest.mock import Base
9
10
from bliss.config.plugins.utils import find_top_class_and_node
from bliss.controllers.bliss_controller import BlissController
11
12
13
14
15
16
17
18
19
20


def create_objects_from_config_node(cfg_obj, cfg_node):

    """
        Create an object from the config with a given name (unique). 
        It ensures that the controller and sub-objects are only created once.
        
        This function resolves dependencies between the BlissController and its sub-objects with a name.
        It looks for the 'class' key in 'cfg_node' (or at upper levels) to instantiate the BlissController.
21
        All sub-configs of named sub-objects owned by the controller are stored as cached items for later instantiation via config.get.
22
23
24
25
26
27

        args:
            cfg_obj: a Config object (from config.static)
            cfg_node: a ConfigNode object (from config.static)

        yield: 
28
            tuple: (created_items, cached_items)
29
30
31
    """

    # search the 'class' key in cfg_node or at a upper node level
32
33
34
    # then return the class and the associated config node
    klass, ctrl_node = find_top_class_and_node(cfg_node)
    ctrl_name = ctrl_node.get("name")  # ctrl could have a name in config
35
36
37
    item_name = cfg_node["name"]  # name of the item that should be created and returned

    # always create the bliss controller first
38
39
    bctrl = klass(ctrl_node)

Perceval Guillou's avatar
Perceval Guillou committed
40
    # print(f"\n=== From config: {item_name} from {bctrl.name}")
41
42
43

    if isinstance(bctrl, BlissController):

44
45
46
47
48
        # prepare subitems configs and cache item's controller.
        # the controller decides which items should be cached and which controller
        # is associated to the cached item (in case the cached item is owned by a sub-controller of this controller)
        cacheditemnames2ctrl = bctrl._prepare_subitems_configs()
        # print(f"\n=== Caching: {list(cacheditemnames2ctrl.keys())} from {bctrl.name}")
49

50
        # --- add the controller to registered items (if it has a name)
51
52
53
54
        name2items = {}
        if ctrl_name:
            name2items[ctrl_name] = bctrl

55
        # update the config cache dict now to avoid cyclic instantiation with internal references
56
57
        # an internal reference occurs when a subitem config uses a reference to another subitem owned by the same controller.
        yield name2items, cacheditemnames2ctrl
58
59

        # load config and init controller
60
61
62
63
64
65
66
67
68
        try:
            bctrl._controller_init()
        except BaseException:
            # remove cached obj if controller initialization fails (to avoid items with different instances of the same controller)
            for iname in cacheditemnames2ctrl.keys():
                cfg_obj._name2cache.pop(iname, None)
            raise

        # --- don't forget to instantiate the object for which this function has been called (if not a controller)
69
70
71
72
73
        if item_name != ctrl_name:
            obj = cfg_obj.get(item_name)
            yield {item_name: obj}

        # --- Now any new object_name going through 'config.get( obj_name )' should call 'create_object_from_cache' only.
74
        # --- 'create_objects_from_config_node' should never be called again for any object related to the controller instantiated here (see config.get code)
75
76
77
78
79
80
81
82
83
84
85

    elif (
        item_name == ctrl_name
    ):  # allow instantiation of top object which is not a BlissController
        yield {ctrl_name: bctrl}
        return
    else:  # prevent instantiation of an item comming from a top controller which is not a BlissController
        raise TypeError(f"{bctrl} is not a BlissController object!")


def create_object_from_cache(config, name, bctrl):
Perceval Guillou's avatar
Perceval Guillou committed
86
    # print(f"\n=== From cache: {name} from {bctrl.name}")
87
88
89
90
91
92
93

    try:
        return bctrl._get_subitem(name)
    except BaseException:
        # put back item in cached items if instantiation has failed
        config._name2cache[name] = bctrl
        raise