Skip to content

Commit a00a66b

Browse files
authored
Merge pull request #2851 from cta-observatory/fix_tool_current_config
treat list[Component] in get_current_config()
2 parents 04a1ec7 + e2364fb commit a00a66b

4 files changed

Lines changed: 99 additions & 13 deletions

File tree

docs/changes/2851.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed bug in `ctapipe.core.Tool.get_current_config` where ``Tools`` containing lists of
2+
``Components`` did not get registered, and thus their configs did not end up in the
3+
provenance log.

src/ctapipe/core/component.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
""" Class to handle configuration for algorithms """
1+
"""Class to handle configuration for algorithms"""
2+
23
import html
34
import warnings
45
import weakref
@@ -226,12 +227,22 @@ def get_current_config(self):
226227
"""return the current configuration as a dict (e.g. the values
227228
of all traits, even if they were not set during configuration)
228229
"""
230+
from .tool import Tool # to avoid circular import
231+
229232
name = self.__class__.__name__
230233
config = {name: {k: v.get(self) for k, v in self.traits(config=True).items()}}
231234

232235
for val in self.__dict__.values():
233-
if isinstance(val, Component):
236+
if isinstance(val, (Component, Tool)):
234237
config[name].update(val.get_current_config())
238+
if (
239+
isinstance(val, (list, tuple))
240+
and val
241+
and isinstance(val[0], (Component, Tool))
242+
):
243+
for element in val:
244+
if isinstance(element, (Component, Tool)):
245+
config[name].update(element.get_current_config())
235246

236247
return config
237248

src/ctapipe/core/tests/test_tool.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,46 @@ def setup(self):
199199
assert current_config["MyTool"]["userparam"] == 2.0
200200

201201

202+
def test_tool_current_config_subcomponents_list():
203+
"""Check that we can get the full instance configuration for tools that
204+
contain lists of subcomponents (which can be tools)"""
205+
from ctapipe.core.component import Component
206+
207+
class SubComponent(Component):
208+
param = Int(default_value=3).tag(config=True)
209+
210+
class SubComponent2(Component):
211+
param = Int(default_value=3).tag(config=True)
212+
213+
class MyComponent(Component):
214+
val = Int(default_value=42).tag(config=True)
215+
216+
def __init__(self, config=None, parent=None):
217+
super().__init__(config=config, parent=parent)
218+
self.subs = [SubComponent(parent=self), SubComponent2(parent=self)]
219+
220+
class MyTool(Tool):
221+
description = "test"
222+
userparam = Float(5.0, help="parameter").tag(config=True)
223+
224+
def setup(self):
225+
self.my_comp = MyComponent(parent=self)
226+
227+
config = Config()
228+
config.MyTool.userparam = 2.0
229+
config.MyTool.MyComponent.val = 10
230+
config.MyTool.MyComponent.SubComponent.param = -1
231+
232+
tool = MyTool(config=config)
233+
tool.setup()
234+
235+
current_config = tool.get_current_config()
236+
assert current_config["MyTool"]["MyComponent"]["val"] == 10
237+
assert current_config["MyTool"]["MyComponent"]["SubComponent"]["param"] == -1
238+
assert current_config["MyTool"]["MyComponent"]["SubComponent2"]["param"] == 3
239+
assert current_config["MyTool"]["userparam"] == 2.0
240+
241+
202242
def test_tool_exit_code():
203243
"""Check that we can get the full instance configuration"""
204244

@@ -665,3 +705,45 @@ class SomeTool(Tool):
665705
# test correct case:
666706
tool.load_config_file(good_conf_path)
667707
assert tool.float_option > 1
708+
709+
710+
def test_tool_in_tool():
711+
"""Check that a Tool that calls other Tools has the right config in the
712+
provenance log."""
713+
714+
class InnerTool(Tool):
715+
param1 = traits.Integer(12).tag(config=True)
716+
717+
def start(self):
718+
print(f"started inner: {self.get_current_config()}")
719+
720+
class CompoundTool(Tool):
721+
param1 = traits.Integer(12).tag(config=True)
722+
filename = traits.Unicode().tag(config=True)
723+
724+
classes = [
725+
InnerTool,
726+
]
727+
728+
def setup(self):
729+
self._inner = InnerTool(parent=self)
730+
731+
def start(self):
732+
print(f"started calib: {self.get_current_config()}")
733+
734+
run_tool(self._inner)
735+
736+
conf = Config()
737+
conf.InnerTool.param1 = 6
738+
conf.CompoundTool.filename = "test.txt"
739+
conf.CompoundTool.param1 = 100
740+
741+
tool = CompoundTool(config=conf)
742+
run_tool(tool, raises=False) # have to run it for setup()
743+
744+
assert "InnerTool" in tool.config
745+
746+
current_config = tool.get_current_config()
747+
assert "CompoundTool" in current_config
748+
assert "InnerTool" in current_config["CompoundTool"]
749+
assert current_config["CompoundTool"]["InnerTool"]["param1"] == 6

src/ctapipe/core/tool.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -505,17 +505,7 @@ def get_current_config(self):
505505
"""return the current configuration as a dict (e.g. the values
506506
of all traits, even if they were not set during configuration)
507507
"""
508-
conf = {
509-
self.__class__.__name__: {
510-
k: v.get(self) for k, v in self.traits(config=True).items()
511-
}
512-
}
513-
514-
for val in self.__dict__.values():
515-
if isinstance(val, Component):
516-
conf[self.__class__.__name__].update(val.get_current_config())
517-
518-
return conf
508+
return Component.get_current_config(self)
519509

520510
def _repr_html_(self):
521511
"""nice HTML rep, with blue for non-default values"""

0 commit comments

Comments
 (0)