LSSTApplications  20.0.0
LSSTDataManagementBasePackage
configOverrides.py
Go to the documentation of this file.
1 # This file is part of pipe_base.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (http://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 
22 """Module which defines ConfigOverrides class and related methods.
23 """
24 
25 __all__ = ["ConfigOverrides"]
26 
27 import ast
28 
29 import lsst.pex.exceptions as pexExceptions
30 from lsst.utils import doImport
31 
32 from enum import Enum
33 
34 OverrideTypes = Enum("OverrideTypes", "Value File Python Instrument")
35 
36 
38  """Defines a set of overrides to be applied to a task config.
39 
40  Overrides for task configuration need to be applied by activator when
41  creating task instances. This class represents an ordered set of such
42  overrides which activator receives from some source (e.g. command line
43  or some other configuration).
44 
45  Methods
46  ----------
47  addFileOverride(filename)
48  Add overrides from a specified file.
49  addValueOverride(field, value)
50  Add override for a specific field.
51  applyTo(config)
52  Apply all overrides to a `config` instance.
53 
54  Notes
55  -----
56  Serialization support for this class may be needed, will add later if
57  necessary.
58  """
59 
60  def __init__(self):
61  self._overrides = []
62 
63  def addFileOverride(self, filename):
64  """Add overrides from a specified file.
65 
66  Parameters
67  ----------
68  filename : str
69  Path to the override file.
70  """
71  self._overrides.append((OverrideTypes.File, filename))
72 
73  def addValueOverride(self, field, value):
74  """Add override for a specific field.
75 
76  This method is not very type-safe as it is designed to support
77  use cases where input is given as string, e.g. command line
78  activators. If `value` has a string type and setting of the field
79  fails with `TypeError` the we'll attempt `eval()` the value and
80  set the field with that value instead.
81 
82  Parameters
83  ----------
84  field : str
85  Fully-qualified field name.
86  value :
87  Value to be given to a filed.
88  """
89  self._overrides.append((OverrideTypes.Value, (field, value)))
90 
91  def addPythonOverride(self, python_snippet: str):
92  """Add Overrides by running a snippit of python code against a config.
93 
94  Parameters
95  ----------
96  python_snippet: str
97  A string which is valid python code to be executed. This is done with
98  config as the only local accessible value.
99  """
100  self._overrides.append((OverrideTypes.Python, python_snippet))
101 
102  def addInstrumentOverride(self, instrument: str, task_name: str):
103  """Apply any overrides that an instrument has for a task
104 
105  Parameters
106  ----------
107  instrument: str
108  A string containing the fully qualified name of an instrument from
109  which configs should be loaded and applied
110  task_name: str
111  The _DefaultName of a task associated with a config, used to look
112  up overrides from the instrument.
113  """
114  instrument_lib = doImport(instrument)()
115  self._overrides.append((OverrideTypes.Instrument, (instrument_lib, task_name)))
116 
117  def applyTo(self, config):
118  """Apply all overrides to a task configuration object.
119 
120  Parameters
121  ----------
122  config : `pex.Config`
123 
124  Raises
125  ------
126  `Exception` is raised if operations on configuration object fail.
127  """
128  for otype, override in self._overrides:
129  if otype is OverrideTypes.File:
130  config.load(override)
131  elif otype is OverrideTypes.Value:
132  field, value = override
133  field = field.split('.')
134  # find object with attribute to set, throws if we name is wrong
135  obj = config
136  for attr in field[:-1]:
137  obj = getattr(obj, attr)
138  # If input is a string and field type is not a string then we
139  # have to convert string to an expected type. Implementing
140  # full string parser is non-trivial so we take a shortcut here
141  # and `eval` the string and assign the resulting value to a
142  # field. Type erroes can happen during both `eval` and field
143  # assignment.
144  if isinstance(value, str) and obj._fields[field[-1]].dtype is not str:
145  try:
146  # use safer ast.literal_eval, it only supports literals
147  value = ast.literal_eval(value)
148  except Exception:
149  # eval failed, wrap exception with more user-friendly message
150  raise pexExceptions.RuntimeError(f"Unable to parse `{value}' into a Python object")
151 
152  # this can throw in case of type mismatch
153  setattr(obj, field[-1], value)
154  elif otype is OverrideTypes.Python:
155  exec(override, None, {"config": config})
156  elif otype is OverrideTypes.Instrument:
157  instrument, name = override
158  instrument.applyConfigOverrides(name, config)
lsst.pipe.base.configOverrides.ConfigOverrides
Definition: configOverrides.py:37
lsst.pipe.base.configOverrides.ConfigOverrides.addInstrumentOverride
def addInstrumentOverride(self, str instrument, str task_name)
Definition: configOverrides.py:102
ast::append
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
lsst.pipe.base.configOverrides.ConfigOverrides.applyTo
def applyTo(self, config)
Definition: configOverrides.py:117
lsst::daf::persistence.utils.doImport
def doImport(pythonType)
Definition: utils.py:104
lsst::utils
Definition: Backtrace.h:29
lsst.pipe.base.configOverrides.ConfigOverrides.addValueOverride
def addValueOverride(self, field, value)
Definition: configOverrides.py:73
lsst.pipe.base.configOverrides.ConfigOverrides._overrides
_overrides
Definition: configOverrides.py:61
lsst.pipe.base.configOverrides.ConfigOverrides.__init__
def __init__(self)
Definition: configOverrides.py:60
lsst.pipe.base.configOverrides.ConfigOverrides.addPythonOverride
def addPythonOverride(self, str python_snippet)
Definition: configOverrides.py:91
lsst::pex::exceptions
Definition: Exception.h:37
lsst::pex::exceptions::RuntimeError
Reports errors that are due to events beyond the control of the program.
Definition: Runtime.h:104
lsst.pipe.base.configOverrides.ConfigOverrides.addFileOverride
def addFileOverride(self, filename)
Definition: configOverrides.py:63