LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
Functions | Variables
lsst.pex.config.wrap Namespace Reference

Functions

def makeConfigClass (ctrl, name=None, base=Config, doc=None, module=None, cls=None)
 
def wrap (ctrl)
 

Variables

 Control
 
 makeControl
 
 readControl
 
 setDefaults
 
 validate
 

Function Documentation

◆ makeConfigClass()

def lsst.pex.config.wrap.makeConfigClass (   ctrl,
  name = None,
  base = Config,
  doc = None,
  module = None,
  cls = None 
)
Create a `~lsst.pex.config.Config` class that matches a  C++ control
object class.

See the `wrap` decorator as a convenient interface to ``makeConfigClass``.

Parameters
----------
ctrl : class
    C++ control class to wrap.
name : `str`, optional
    Name of the new config class; defaults to the ``__name__`` of the
    control class with ``'Control'`` replaced with ``'Config'``.
base : `lsst.pex.config.Config`-type, optional
    Base class for the config class.
doc : `str`, optional
    Docstring for the config class.
module : object, `str`, `int`, or `None` optional
    Either a module object, a string specifying the name of the module, or
    an integer specifying how far back in the stack to look for the module
    to use: 0 is the immediate caller of `~lsst.pex.config.wrap`. This will
    be used to set ``__module__`` for the new config class, and the class
    will also be added to the module. Ignored if `None` or if ``cls`` is
    not `None`. Defaults to None in which case module is looked up from the
    module of ctrl.
cls : class
    An existing config class to use instead of creating a new one; name,
    base doc, and module will be ignored if this is not `None`.

Notes
-----
To use ``makeConfigClass``, write a control object in C++ using the
``LSST_CONTROL_FIELD`` macro in ``lsst/pex/config.h`` (note that it must
have sensible default constructor):

.. code-block:: cpp

   // myHeader.h

   struct InnerControl {
       LSST_CONTROL_FIELD(wim, std::string,
                          "documentation for field 'wim'");
   };

   struct FooControl {
       LSST_CONTROL_FIELD(bar, int, "documentation for field 'bar'");
       LSST_CONTROL_FIELD(baz, double, "documentation for field 'baz'");
       LSST_NESTED_CONTROL_FIELD(zot, myWrappedLib, InnerControl,
                                 "documentation for field 'zot'");

       FooControl() : bar(0), baz(0.0) {}
   };

You can use ``LSST_NESTED_CONTROL_FIELD`` to nest control objects. Wrap
those control objects as you would any other C++ class, but make sure you
include ``lsst/pex/config.h`` before including the header file where
the control object class is defined.

Next, in Python:

.. code-block:: py

   import lsst.pex.config
   import myWrappedLib

   InnerConfig = lsst.pex.config.makeConfigClass(myWrappedLib.InnerControl)
   FooConfig = lsst.pex.config.makeConfigClass(myWrappedLib.FooControl)

This does the following things:

- Adds ``bar``, ``baz``, and ``zot`` fields to ``FooConfig``.
- Set ``FooConfig.Control`` to ``FooControl``.
- Adds ``makeControl`` and ``readControl`` methods to create a
  ``FooControl`` and set the ``FooConfig`` from the ``FooControl``,
  respectively.
- If ``FooControl`` has a ``validate()`` member function,
  a custom ``validate()`` method will be added to ``FooConfig`` that uses
  it.

All of the above are done for ``InnerConfig`` as well.

Any field that would be injected that would clash with an existing
attribute of the class is be silently ignored. This allows you to
customize fields and inherit them from wrapped control classes. However,
these names are still be processed when converting between config and
control classes, so they should generally be present as base class fields
or other instance attributes or descriptors.

While ``LSST_CONTROL_FIELD`` will work for any C++ type, automatic
`~lsst.pex.config.Config` generation only supports ``bool``, ``int``,
``std::int64_t``, ``double``, and ``std::string`` fields, along with
``std::list`` and ``std::vectors`` of those types.

See also
--------
wrap

Definition at line 56 of file wrap.py.

56def makeConfigClass(ctrl, name=None, base=Config, doc=None, module=None, cls=None):
57 """Create a `~lsst.pex.config.Config` class that matches a C++ control
58 object class.
59
60 See the `wrap` decorator as a convenient interface to ``makeConfigClass``.
61
62 Parameters
63 ----------
64 ctrl : class
65 C++ control class to wrap.
66 name : `str`, optional
67 Name of the new config class; defaults to the ``__name__`` of the
68 control class with ``'Control'`` replaced with ``'Config'``.
69 base : `lsst.pex.config.Config`-type, optional
70 Base class for the config class.
71 doc : `str`, optional
72 Docstring for the config class.
73 module : object, `str`, `int`, or `None` optional
74 Either a module object, a string specifying the name of the module, or
75 an integer specifying how far back in the stack to look for the module
76 to use: 0 is the immediate caller of `~lsst.pex.config.wrap`. This will
77 be used to set ``__module__`` for the new config class, and the class
78 will also be added to the module. Ignored if `None` or if ``cls`` is
79 not `None`. Defaults to None in which case module is looked up from the
80 module of ctrl.
81 cls : class
82 An existing config class to use instead of creating a new one; name,
83 base doc, and module will be ignored if this is not `None`.
84
85 Notes
86 -----
87 To use ``makeConfigClass``, write a control object in C++ using the
88 ``LSST_CONTROL_FIELD`` macro in ``lsst/pex/config.h`` (note that it must
89 have sensible default constructor):
90
91 .. code-block:: cpp
92
93 // myHeader.h
94
95 struct InnerControl {
96 LSST_CONTROL_FIELD(wim, std::string,
97 "documentation for field 'wim'");
98 };
99
100 struct FooControl {
101 LSST_CONTROL_FIELD(bar, int, "documentation for field 'bar'");
102 LSST_CONTROL_FIELD(baz, double, "documentation for field 'baz'");
103 LSST_NESTED_CONTROL_FIELD(zot, myWrappedLib, InnerControl,
104 "documentation for field 'zot'");
105
106 FooControl() : bar(0), baz(0.0) {}
107 };
108
109 You can use ``LSST_NESTED_CONTROL_FIELD`` to nest control objects. Wrap
110 those control objects as you would any other C++ class, but make sure you
111 include ``lsst/pex/config.h`` before including the header file where
112 the control object class is defined.
113
114 Next, in Python:
115
116 .. code-block:: py
117
118 import lsst.pex.config
119 import myWrappedLib
120
121 InnerConfig = lsst.pex.config.makeConfigClass(myWrappedLib.InnerControl)
122 FooConfig = lsst.pex.config.makeConfigClass(myWrappedLib.FooControl)
123
124 This does the following things:
125
126 - Adds ``bar``, ``baz``, and ``zot`` fields to ``FooConfig``.
127 - Set ``FooConfig.Control`` to ``FooControl``.
128 - Adds ``makeControl`` and ``readControl`` methods to create a
129 ``FooControl`` and set the ``FooConfig`` from the ``FooControl``,
130 respectively.
131 - If ``FooControl`` has a ``validate()`` member function,
132 a custom ``validate()`` method will be added to ``FooConfig`` that uses
133 it.
134
135 All of the above are done for ``InnerConfig`` as well.
136
137 Any field that would be injected that would clash with an existing
138 attribute of the class is be silently ignored. This allows you to
139 customize fields and inherit them from wrapped control classes. However,
140 these names are still be processed when converting between config and
141 control classes, so they should generally be present as base class fields
142 or other instance attributes or descriptors.
143
144 While ``LSST_CONTROL_FIELD`` will work for any C++ type, automatic
145 `~lsst.pex.config.Config` generation only supports ``bool``, ``int``,
146 ``std::int64_t``, ``double``, and ``std::string`` fields, along with
147 ``std::list`` and ``std::vectors`` of those types.
148
149 See also
150 --------
151 wrap
152 """
153 if name is None:
154 if "Control" not in ctrl.__name__:
155 raise ValueError("Cannot guess appropriate Config class name for %s." % ctrl)
156 name = ctrl.__name__.replace("Control", "Config")
157 if cls is None:
158 cls = type(name, (base,), {"__doc__": doc})
159 if module is not None:
160 # Not only does setting __module__ make Python pretty-printers
161 # more useful, it's also necessary if we want to pickle Config
162 # objects.
163 if isinstance(module, int):
164 frame = getCallerFrame(module)
165 moduleObj = inspect.getmodule(frame)
166 moduleName = moduleObj.__name__
167 elif isinstance(module, str):
168 moduleName = module
169 moduleObj = __import__(moduleName)
170 else:
171 moduleObj = module
172 moduleName = moduleObj.__name__
173 cls.__module__ = moduleName
174 setattr(moduleObj, name, cls)
175 else:
176 cls.__module__ = ctrl.__module__
177 moduleName = ctrl.__module__
178 else:
179 moduleName = cls.__module__
180 if doc is None:
181 doc = ctrl.__doc__
182 fields = {}
183 # loop over all class attributes, looking for the special static methods
184 # that indicate a field defined by one of the macros in pex/config.h.
185 for attr in dir(ctrl):
186 if attr.startswith("_type_"):
187 k = attr[len("_type_") :]
188 getDoc = "_doc_" + k
189 getModule = "_module_" + k
190 getType = attr
191 if hasattr(ctrl, k) and hasattr(ctrl, getDoc):
192 doc = getattr(ctrl, getDoc)()
193 ctype = getattr(ctrl, getType)()
194 if hasattr(ctrl, getModule): # if this is present, it's a nested control object
195 nestedModuleName = getattr(ctrl, getModule)()
196 if nestedModuleName == moduleName:
197 nestedModuleObj = moduleObj
198 else:
199 nestedModuleObj = importlib.import_module(nestedModuleName)
200 try:
201 dtype = getattr(nestedModuleObj, ctype).ConfigClass
202 except AttributeError:
203 raise AttributeError("'%s.%s.ConfigClass' does not exist" % (moduleName, ctype))
204 fields[k] = ConfigField(doc=doc, dtype=dtype)
205 else:
206 try:
207 dtype = _dtypeMap[ctype]
208 FieldCls = Field
209 except KeyError:
210 dtype = None
211 m = _containerRegex.match(ctype)
212 if m:
213 dtype = _dtypeMap.get(m.group("type"), None)
214 FieldCls = ListField
215 if dtype is None:
216 raise TypeError("Could not parse field type '%s'." % ctype)
217 fields[k] = FieldCls(doc=doc, dtype=dtype, optional=True)
218
219 # Define a number of methods to put in the new Config class. Note that
220 # these are "closures"; they have access to local variables defined in
221 # the makeConfigClass function (like the fields dict).
222 def makeControl(self):
223 """Construct a C++ Control object from this Config object.
224
225 Fields set to `None` will be ignored, and left at the values defined
226 by the Control object's default constructor.
227 """
228 r = self.Control()
229 for k, f in fields.items():
230 value = getattr(self, k)
231 if isinstance(f, ConfigField):
232 value = value.makeControl()
233 if value is not None:
234 if isinstance(value, List):
235 setattr(r, k, value._list)
236 else:
237 setattr(r, k, value)
238 return r
239
240 def readControl(self, control, __at=None, __label="readControl", __reset=False):
241 """Read values from a C++ Control object and assign them to self's
242 fields.
243
244 Parameters
245 ----------
246 control
247 C++ Control object.
248
249 Notes
250 -----
251 The ``__at``, ``__label``, and ``__reset`` arguments are for internal
252 use only; they are used to remove internal calls from the history.
253 """
254 if __at is None:
255 __at = getCallStack()
256 values = {}
257 for k, f in fields.items():
258 if isinstance(f, ConfigField):
259 getattr(self, k).readControl(getattr(control, k), __at=__at, __label=__label, __reset=__reset)
260 else:
261 values[k] = getattr(control, k)
262 if __reset:
263 self._history = {}
264 self.update(__at=__at, __label=__label, **values)
265
266 def validate(self):
267 """Validate the config object by constructing a control object and
268 using a C++ ``validate()`` implementation.
269 """
270 super(cls, self).validate()
271 r = self.makeControl()
272 r.validate()
273
274 def setDefaults(self):
275 """Initialize the config object, using the Control objects default ctor
276 to provide defaults.
277 """
278 super(cls, self).setDefaults()
279 try:
280 r = self.Control()
281 # Indicate in the history that these values came from C++, even
282 # if we can't say which line
283 self.readControl(
284 r,
285 __at=[StackFrame(ctrl.__name__ + " C++", 0, "setDefaults", "")],
286 __label="defaults",
287 __reset=True,
288 )
289 except Exception:
290 pass # if we can't instantiate the Control, don't set defaults
291
292 ctrl.ConfigClass = cls
293 cls.Control = ctrl
294 cls.makeControl = makeControl
295 cls.readControl = readControl
296 cls.setDefaults = setDefaults
297 if hasattr(ctrl, "validate"):
298 cls.validate = validate
299 for k, field in fields.items():
300 if not hasattr(cls, k):
301 setattr(cls, k, field)
302 return cls
303
304
table::Key< int > type
Definition: Detector.cc:163
table::Key< int > to
table::Key< int > a
#define LSST_NESTED_CONTROL_FIELD(NAME, MODULE, TYPE, DOC)
A preprocessor macro used to define fields in C++ "control object" structs, for nested control object...
Definition: config.h:69
#define LSST_CONTROL_FIELD(NAME, TYPE, DOC)
A preprocessor macro used to define fields in C++ "control object" structs.
Definition: config.h:43
def getCallStack(skip=0)
Definition: callStack.py:174
def getCallerFrame(relative=0)
Definition: callStack.py:34
def makeConfigClass(ctrl, name=None, base=Config, doc=None, module=None, cls=None)
Definition: wrap.py:56
def wrap(ctrl)
Definition: wrap.py:305
bool defined
Definition: slots.cc:27

◆ wrap()

def lsst.pex.config.wrap.wrap (   ctrl)
Decorator that adds fields from a C++ control class to a
`lsst.pex.config.Config` class.

Parameters
----------
ctrl : object
    The C++ control class.

Notes
-----
See `makeConfigClass` for more information. This `wrap` decorator is
equivalent to calling `makeConfigClass` with the decorated class as the
``cls`` argument.

Examples
--------
Use `wrap` like this::

    @wrap(MyControlClass)
    class MyConfigClass(Config):
        pass

See also
--------
makeConfigClass

Definition at line 305 of file wrap.py.

305def wrap(ctrl):
306 """Decorator that adds fields from a C++ control class to a
308
309 Parameters
310 ----------
311 ctrl : object
312 The C++ control class.
313
314 Notes
315 -----
316 See `makeConfigClass` for more information. This `wrap` decorator is
317 equivalent to calling `makeConfigClass` with the decorated class as the
318 ``cls`` argument.
319
320 Examples
321 --------
322 Use `wrap` like this::
323
324 @wrap(MyControlClass)
325 class MyConfigClass(Config):
326 pass
327
328 See also
329 --------
330 makeConfigClass
331 """
332
333 def decorate(cls):
334 return makeConfigClass(ctrl, cls=cls)
335
336 return decorate

Variable Documentation

◆ Control

lsst.pex.config.wrap.Control

Definition at line 293 of file wrap.py.

◆ makeControl

lsst.pex.config.wrap.makeControl

Definition at line 294 of file wrap.py.

◆ readControl

lsst.pex.config.wrap.readControl

Definition at line 295 of file wrap.py.

◆ setDefaults

lsst.pex.config.wrap.setDefaults

Definition at line 296 of file wrap.py.

◆ validate

lsst.pex.config.wrap.validate

Definition at line 298 of file wrap.py.