Commit 894c5160 authored by Niklas Stefan Nolte's avatar Niklas Stefan Nolte 🔥 Committed by Rosen Matev
Browse files

Make tools stateless

parent be5cae67
......@@ -401,8 +401,6 @@ class Algorithm(object):
instance._requireIOV = require_IOVLock
instance._tools = _tools
instance._outputs_define_identity = bool(forced_locations)
for tool in _tools.values():
tool.set_parent(instance)
#make OUTPUTS ############# these were not needed for calculation of the ID (only the forced locations)
def _make_outs(outputs):
......@@ -520,11 +518,11 @@ class Algorithm(object):
config.update(inp.producer.configuration())
for tool in self.tools.values():
config.update(tool.configuration())
config.update(tool.configuration(self.name))
#props
config[self] = self.properties.copy()
config[self][config.iovlockkey] = self._requireIOV # FIXME
cfg = config[(self.type, self.name)] = self.properties.copy()
cfg[config.iovlockkey] = self._requireIOV # FIXME
#io
input_dict = _gather_locations(self.inputs)
......@@ -536,12 +534,12 @@ class Algorithm(object):
if self._output_transform:
output_dict = self._output_transform(**output_dict)
config[self].update(input_dict)
config[self].update(output_dict)
cfg.update(input_dict)
cfg.update(output_dict)
#tools
tools_dict = _gather_tool_names(self.tools)
config[self].update(tools_dict)
cfg.update(tools_dict)
return config
......@@ -712,7 +710,6 @@ class Tool(object):
The make_tool method should be used to create Tool wrappers from
Configurables. Importing from PyConf.Tools does this for you.
"""
_default_parent = 'ToolSvc'
_tool_store = dict()
......@@ -760,39 +757,25 @@ class Tool(object):
except KeyError:
instance = super(Tool, cls).__new__(cls)
instance._id = identity
instance._parent = None
instance._tool_type = tool_type
instance._name = _get_unique_name(name or tool_type.getType())
instance._private = False
instance._inputs = _inputs
instance._properties = _properties
instance._tools = _tools
for tool in instance._tools.values():
tool.set_parent(instance)
instance._readonly = True
cls._tool_store[identity] = instance
return instance
@staticmethod
def _calc_id(typename, props, inputs, tools, parent=None):
def _calc_id(typename, props, inputs, tools):
props_hash = _hash_dict(props)
inputs_hash = _hash_dict(
{key: _datahandle_ids(handles)
for key, handles in inputs.items()})
tools_hash = _hash_dict({key: tool.id for key, tool in tools.items()})
to_be_hashed = [typename, props_hash, inputs_hash, tools_hash]
if parent:
to_be_hashed.append(parent.id)
return _hash_list(to_be_hashed)
@property
def is_private(self):
return self._private
@property
def parent(self):
return self._parent
@property
def inputs(self):
return self._inputs
......@@ -814,10 +797,9 @@ class Tool(object):
#how instances refer to their tools
return self.typename + '/' + self._name
@property
def name(self):
def name(self, parent=None):
#configuration name of the tool itself
return (self.parent.name if self.parent else self._default_parent) + \
return (parent or "ToolSvc") + \
'.' + self._name
@property
......@@ -828,36 +810,17 @@ class Tool(object):
def typename(self):
return self.type.getType()
def set_parent(self, parent):
if self.parent:
raise ConfigurationError(
"This Tool already has a parent. Please instantiate a new one")
if not is_algorithm(parent) and not is_tool(parent):
raise TypeError("{} not of type Algorithm or Tool".format(parent))
object.__setattr__(self, "_readonly",
False) # this is the only allowed modification
self._parent = parent
self._private = True
del self._tool_store[self._id]
self._id = self._calc_id(self._tool_type, self._properties,
self._inputs, self._tools, parent)
self._tool_store[self._id] = self
object.__setattr__(self, "_readonly", True) # make immutable again
def __repr__(self):
return 'Tool({})'.format(self.name)
return 'Tool({})'.format(self.name())
def __hash__(self):
return self.id
def __eq__(self, other):
return self.id == other.id and self.parent == other.parent
return self.id == other.id
def configuration(self):
if not self.parent and self.is_private:
raise ConfigurationError(
'cannot configure a private tool {} with no parent'.format(
self))
def configuration(self, parent=None):
name = self.name(parent)
config = dataflow_config()
for inputs in self.inputs.values():
......@@ -866,14 +829,14 @@ class Tool(object):
config.update(inp.producer.configuration())
for tool in self.tools.values():
config.update(tool.configuration())
config.update(tool.configuration(name))
config[self] = self.properties.copy()
cfg = config[(self.type, name)] = self.properties.copy()
input_dict = _gather_locations(self.inputs)
config[self].update(input_dict)
cfg.update(input_dict)
tools_dict = _gather_tool_names(self.tools)
config[self].update(tools_dict)
cfg.update(tools_dict)
return config
......
......@@ -41,21 +41,23 @@ class dataflow_config(OrderedDict):
iovlockkey = '__IOVLock__'
def apply(self):
from .components import setup_component, is_algorithm, is_tool
from .components import setup_component, _is_configurable_tool, _is_configurable_algorithm
self.__readonly = True
configurable_algs = []
configurable_tools = []
for item, conf in self.items():
for type_name, conf in self.items():
type_, name = type_name
IOV = conf.pop(self.iovlockkey, False) # FIXME
configurable = setup_component(
item.type, instanceName=item.name, IOVLockDep=IOV, **conf)
if is_algorithm(item):
type_, instanceName=name, IOVLockDep=IOV, **conf)
if _is_configurable_algorithm(type_):
configurable_algs.append(configurable)
elif is_tool(item):
elif _is_configurable_tool(type_):
configurable_tools.append(configurable)
else:
raise TypeError(
"{} neither of type Tool nor Algorithm".format(item))
"{} wants to be configured but is neither of type AlgTool nor Algorithm"
.format(type_))
return configurable_algs, configurable_tools
def print(self):
......
......@@ -120,11 +120,6 @@ class EverythingHandler(object):
def register_public_tool(self, tool):
if not is_tool(tool):
raise TypeError('{} is not of type Tool'.format(tool))
if tool.is_private:
raise ConfigurationError(
"tool {} configured private, but registered public.".format(
tool))
self._tools.append(tool)
def alwaysRunThis(self, alg):
......
......@@ -112,6 +112,14 @@ def test_tool_compat():
ToolConsumer, FloatTool=FloatTool(),
InputLocation=producer) # don't be a fool, wrap your tool
# A Tool instance should be usable in multiple (different) Algorithms
tool = Tool(FloatTool, Input=producer)
alg1 = Algorithm(ToolConsumer, FloatTool=tool, InputLocation=producer)
alg2 = Algorithm(
ToolConsumer, FloatTool=tool, InputLocation=producer,
OutputLevel=0) # make the second alg slightly different
assert alg1.tools == alg2.tools
def algorithm_equal_hashes():
"""Two Algorithms should have equal hashes if their properties and inputs are equal."""
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment