ATLAS Offline Software
Loading...
Searching...
No Matches
atlascp.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2#
3# @author Giordon Stark
4
5# Syntactic sugar for creating CP tools by class name rather than raw
6# type strings. Module-level __getattr__ dispatches attribute access
7# to a constructor factory so that, e.g.:
8#
9# from ColumnarToolWrapperPython import atlascp
10# tool = atlascp.MuonEfficiencyScaleFactors('myTool')
11#
12# is equivalent to:
13#
14# Tool("CP::MuonEfficiencyScaleFactors/myTool")
15
16import uuid
17
18import awkward as ak
19
20from ColumnarToolWrapperPython.tool import Tool
21from ColumnarToolWrapperPython.config import load_config, merge_tool_config
22
23_class_cache = {}
24
25
26def _make_tool_class(tool_type_name):
27 """Return a named Tool subclass for tool_type_name, creating it if needed.
28
29 Parameters
30 ----------
31 tool_type_name:
32 The C++ class name without namespace, e.g. "MuonEfficiencyScaleFactors".
33
34 Returns
35 -------
36 type
37 A subclass of Tool whose ``__name__`` equals ``tool_type_name``.
38 Repeated calls with the same name return the same class object.
39 """
40 if tool_type_name not in _class_cache:
41 _class_cache[tool_type_name] = type(tool_type_name, (Tool,), {})
42 return _class_cache[tool_type_name]
43
44
45def __getattr__(name):
46 """Return a constructor for the named CP tool type.
47
48 Called when code accesses ``atlascp.<ToolTypeName>``. Returns a callable
49 that creates and initializes a ``Tool`` subclass instance.
50
51 Parameters
52 ----------
53 name:
54 Tool type name without namespace, e.g. "MuonEfficiencyScaleFactors".
55
56 Returns
57 -------
58 callable
59 A constructor with signature
60 ``(instance_name=None, /, *, namespace="CP", properties=None, rename_containers=None)``.
61 ``instance_name`` is positional-only and optional; all other arguments are keyword-only.
62 When omitted, an auto-generated unique name is used.
63 """
64 def constructor(instance_name=None, /, *, namespace="CP", properties=None, rename_containers=None):
65 if instance_name is None:
66 instance_name = f"unique{uuid.uuid4().hex[:12]}"
67 type_and_name = f"{namespace}::{name}/{instance_name}"
68 cls = _make_tool_class(name)
69 instance = cls.__new__(cls)
70 Tool.__init__(instance, type_and_name, properties=properties, rename_containers=rename_containers)
71 return instance
72 constructor.__name__ = name
73 constructor.__qualname__ = name
74 return constructor
75
76
78 """Ordered list-like container of configured CP tools.
79
80 Returned by ``atlascp.configure()``. Multiple tools of the same type are
81 supported. Iterating yields ``(tool_type_name, Tool)`` pairs in config
82 file order.
83
84 Use ``corrections[type_name]`` to get a list of all tools of that type.
85 Use ``corrections.apply(events)`` to run all tools and merge outputs.
86 """
87
88 def __init__(self, tools):
89 self._tools = list(tools) # list of (tool_type_name, Tool)
90
91 def __getitem__(self, tool_type_name):
92 """Return list of tools matching tool_type_name."""
93 matches = [tool for name, tool in self._tools if name == tool_type_name]
94 if not matches:
95 raise KeyError(tool_type_name)
96 return matches
97
98 def __contains__(self, tool_type_name):
99 return any(name == tool_type_name for name, _ in self._tools)
100
101 def __iter__(self):
102 return iter(name for name, _ in self._tools)
103
104 def items(self):
105 return iter(self._tools)
106
107 def apply(self, events):
108 """Run all tools on events and return merged output columns.
109
110 Parameters
111 ----------
112 events:
113 An ak.Array with fields matching the configured containers.
114
115 Returns
116 -------
117 ak.Array
118 Record array with all output columns from all tools merged.
119 """
120 outputs = {}
121 for _name, tool in self._tools:
122 result = tool(events)
123 for field in ak.fields(result):
124 outputs[field] = result[field]
125 return ak.Array(outputs)
126
127
128def configure(path, *, namespace="CP"):
129 """Load a TOML config file and return an initialized Corrections object.
130
131 The ``[global]`` section provides default ``rename_containers`` and
132 ``properties``. Tool entries are defined as ``[[tool.TypeName]]`` blocks;
133 multiple blocks of the same type create multiple tool instances.
134
135 Parameters
136 ----------
137 path:
138 Path to a TOML configuration file (str or Path).
139 namespace:
140 C++ namespace for all tools (default ``"CP"``).
141
142 Returns
143 -------
144 Corrections
145 """
146 cfg = load_config(path)
147 global_cfg = cfg.get("global", {})
148 tools = []
149 for type_name, entries in cfg.get("tool", {}).items():
150 for entry in entries:
151 merged = merge_tool_config(global_cfg, entry)
152 constructor = __getattr__(type_name)
153 tool = constructor(
154 namespace=namespace,
155 properties=merged["properties"] or None,
156 rename_containers=merged["rename_containers"] or None,
157 )
158 tools.append((type_name, tool))
159 return Corrections(tools)
__init__(self, tools)
Definition atlascp.py:88
__getitem__(self, tool_type_name)
Definition atlascp.py:91
__contains__(self, tool_type_name)
Definition atlascp.py:98
__getattr__(name)
Definition atlascp.py:45
_make_tool_class(tool_type_name)
Definition atlascp.py:26
configure(path, *, namespace="CP")
Definition atlascp.py:128