lsst-pex-config 25.0.1rc4__tar.gz → 29.2025.4900__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. lsst_pex_config-29.2025.4900/COPYRIGHT +4 -0
  2. {lsst-pex-config-25.0.1rc4/python/lsst_pex_config.egg-info → lsst_pex_config-29.2025.4900}/PKG-INFO +13 -8
  3. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/CHANGES.rst +45 -1
  4. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/field-types.rst +43 -3
  5. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/index.rst +4 -0
  6. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/inspecting-configs.rst +1 -1
  7. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/overview.rst +14 -2
  8. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/registry-intro.rst +6 -6
  9. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/wrapping-cpp-control-objects.rst +1 -1
  10. lsst_pex_config-29.2025.4900/pyproject.toml +196 -0
  11. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/callStack.py +8 -8
  12. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/choiceField.py +5 -4
  13. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/comparison.py +17 -13
  14. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/config.py +209 -145
  15. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/configChoiceField.py +87 -63
  16. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/configDictField.py +100 -30
  17. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/configField.py +24 -23
  18. lsst_pex_config-29.2025.4900/python/lsst/pex/config/configurableActions/__init__.py +23 -0
  19. lsst_pex_config-29.2025.4900/python/lsst/pex/config/configurableActions/_configurableAction.py +69 -0
  20. lsst_pex_config-29.2025.4900/python/lsst/pex/config/configurableActions/_configurableActionField.py +122 -0
  21. lsst_pex_config-29.2025.4900/python/lsst/pex/config/configurableActions/_configurableActionStructField.py +478 -0
  22. lsst_pex_config-29.2025.4900/python/lsst/pex/config/configurableActions/tests.py +101 -0
  23. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/configurableField.py +80 -36
  24. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/convert.py +1 -1
  25. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/dictField.py +96 -65
  26. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/history.py +25 -24
  27. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/listField.py +65 -55
  28. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/rangeField.py +7 -7
  29. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/registry.py +83 -34
  30. lsst_pex_config-29.2025.4900/python/lsst/pex/config/version.py +2 -0
  31. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/wrap.py +22 -14
  32. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900/python/lsst_pex_config.egg-info}/PKG-INFO +13 -8
  33. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst_pex_config.egg-info/SOURCES.txt +6 -0
  34. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst_pex_config.egg-info/requires.txt +0 -2
  35. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/setup.cfg +1 -1
  36. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/testLib.py +2 -1
  37. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_Config.py +199 -75
  38. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test__file__.py +2 -0
  39. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_configChoiceField.py +18 -8
  40. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_configDictField.py +48 -11
  41. lsst_pex_config-29.2025.4900/tests/test_configurableActions.py +275 -0
  42. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_configurableField.py +30 -5
  43. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_dictField.py +41 -8
  44. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_history.py +4 -0
  45. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_listField.py +20 -0
  46. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_registry.py +22 -4
  47. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_ticket1911.py +2 -0
  48. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_ticket1914.py +8 -0
  49. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_ticket1915.py +8 -0
  50. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_ticket1929.py +8 -0
  51. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_ticket1995.py +6 -0
  52. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_ticket2818.py +2 -0
  53. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_ticketDM-7337.py +2 -0
  54. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_unloaded_yaml.py +2 -0
  55. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/tests/test_wrap.py +13 -5
  56. lsst-pex-config-25.0.1rc4/COPYRIGHT +0 -3
  57. lsst-pex-config-25.0.1rc4/pyproject.toml +0 -111
  58. lsst-pex-config-25.0.1rc4/python/lsst/pex/config/version.py +0 -2
  59. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/LICENSE +0 -0
  60. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/MANIFEST.in +0 -0
  61. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/README.rst +0 -0
  62. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/bsd_license.txt +0 -0
  63. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/doc/lsst.pex.config/design-notes.rst +0 -0
  64. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/gpl-v3.0.txt +0 -0
  65. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/__init__.py +0 -0
  66. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/__init__.py +0 -0
  67. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/__init__.py +0 -0
  68. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/_doNotImportMe.py +0 -0
  69. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst/pex/config/py.typed +0 -0
  70. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst_pex_config.egg-info/dependency_links.txt +0 -0
  71. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst_pex_config.egg-info/top_level.txt +0 -0
  72. {lsst-pex-config-25.0.1rc4 → lsst_pex_config-29.2025.4900}/python/lsst_pex_config.egg-info/zip-safe +0 -0
@@ -0,0 +1,4 @@
1
+ Copyright 2008-2014, 2016, 2017, 2019, 2020, 2022-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
2
+ Copyright 2014-2023 The Trustees of Princeton University
3
+ Copyright 2015-2016, 2018-2020, 2022-2023 University of Washington
4
+ Copyright 2015, 2018-2020 The Board of Trustees of the Leland Stanford Junior University, through SLAC National Accelerator Laboratory
@@ -1,26 +1,31 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: lsst-pex-config
3
- Version: 25.0.1rc4
3
+ Version: 29.2025.4900
4
4
  Summary: A flexible configuration system using Python files.
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
- License: BSD 3-Clause License, GPLv3+
6
+ License-Expression: BSD-3-Clause OR GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/lsst/pex_config
8
8
  Keywords: lsst
9
9
  Classifier: Intended Audience :: Science/Research
10
- Classifier: License :: OSI Approved :: BSD License
11
- Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
12
10
  Classifier: Operating System :: OS Independent
13
11
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
12
  Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
17
16
  Classifier: Topic :: Scientific/Engineering :: Astronomy
17
+ Requires-Python: >=3.10.0
18
18
  Description-Content-Type: text/x-rst
19
- Provides-Extra: test
20
19
  License-File: COPYRIGHT
21
20
  License-File: LICENSE
22
21
  License-File: gpl-v3.0.txt
23
22
  License-File: bsd_license.txt
23
+ Requires-Dist: pyyaml>=5.1
24
+ Requires-Dist: numpy>=1.17
25
+ Provides-Extra: test
26
+ Requires-Dist: pytest>=3.2; extra == "test"
27
+ Requires-Dist: pytest-openfiles>=0.5.0; extra == "test"
28
+ Dynamic: license-file
24
29
 
25
30
  ##########
26
31
  pex_config
@@ -1,10 +1,54 @@
1
+ lsst-pex-config v29.0.0 (2025-03-25)
2
+ ====================================
3
+
4
+ New Features
5
+ ------------
6
+
7
+ - Introduced the ``keyCheck`` callback to ``DictField`` and ``ConfigDictField``, allowing custom key validation during assignment. Added unit tests to ensure functionality. (`DM-48074 <https://rubinobs.atlassian.net/browse/DM-48074>`_)
8
+ - Make the messages emitted by ``compareConfigs`` consistently start with the fully-qualified name of the config field. (`DM-49121 <https://rubinobs.atlassian.net/browse/DM-49121>`_)
9
+
10
+
11
+ lsst-pex-config v28.0.0 (2024-11-21)
12
+ ====================================
13
+
14
+ No significant changes.
15
+
16
+
17
+ lsst-pex-config v27.0.0 (2024-05-29)
18
+ ====================================
19
+
20
+ - Improved all docstrings to make them compliant with Numpydoc.
21
+ - Minor code cleanups driven by Ruff linting. (`DM-42116 <https://rubinobs.atlassian.net/browse/DM-42116>`_)
22
+
23
+ lsst-pex-config v26.0.0 (2023-09-22)
24
+ ====================================
25
+
26
+ New Features
27
+ ------------
28
+
29
+ - Added a dynamic-default callback argument to ``RegistryField``. (`DM-31924 <https://rubinobs.atlassian.net/browse/DM-31924>`_)
30
+ - Introduced ``ConfigurableActions``. These are ``pex_config`` fields and config types which function as a functor, state is set at config time, but the ``ConfigurableActions`` are callable at runtime to produce an action. (`DM-36649 <https://rubinobs.atlassian.net/browse/DM-36649>`_)
31
+
32
+
33
+ API Changes
34
+ -----------
35
+
36
+ - The ``loadFromStream`` and ``loadFromString`` methods now take a dictionary as an optional argument which allows specifying additional variables to use in local scope when reading configs. (`DM-40198 <https://rubinobs.atlassian.net/browse/DM-40198>`_)
37
+
38
+
39
+ Other Changes and Additions
40
+ ---------------------------
41
+
42
+ - There is now a requirement for configuration parameters to have a non-empty docstring. (`DM-4037 <https://rubinobs.atlassian.net/browse/DM-4037>`_)
43
+
44
+
1
45
  lsst-pex-config v25.0.0 (2023-02-28)
2
46
  ====================================
3
47
 
4
48
  Other Changes and Additions
5
49
  ---------------------------
6
50
 
7
- - Some sorting now happens when saving config files (via DM-33027). (`DM-35060 <https://jira.lsstcorp.org/browse/DM-35060>`_)
51
+ - Some sorting now happens when saving config files (via DM-33027). (`DM-35060 <https://rubinobs.atlassian.net/browse/DM-35060>`_)
8
52
 
9
53
 
10
54
  lsst-pex-config v24.0.0 (2022-08-30)
@@ -7,7 +7,7 @@ Types of configuration fields
7
7
  .. TODO: improve this page to summarize the purpose of each field, and then have a dedicated section for each field. https://jira.lsstcorp.org/browse/DM-17196
8
8
 
9
9
  Attributes of the configuration object must be subclasses of `Field`.
10
- A number of these are predefined: `Field`, `RangeField`, `ChoiceField`, `ListField`, `ConfigField`, `ConfigChoiceField`, `RegistryField` and `ConfigurableField`.
10
+ A number of these are predefined: `Field`, `RangeField`, `ChoiceField`, `ListField`, `ConfigField`, `ConfigChoiceField`, `RegistryField`, `ConfigurableField`, `~.configurableActions.ConfigurableActionField`, and `~.configurableActions.ConfigurableActionStructField`.
11
11
 
12
12
  Example of `RangeField`:
13
13
 
@@ -17,7 +17,7 @@ Example of `RangeField`:
17
17
  """Parameters for controlling background estimation.
18
18
  """
19
19
  binSize = pexConfig.RangeField(
20
- doc=("How large a region of the sky should be "
20
+ doc=("Define the region of the sky size "
21
21
  "used for each background point."),
22
22
  dtype=int, default=256, min=10
23
23
  )
@@ -31,7 +31,7 @@ Example of `ListField` and `Config` inheritance:
31
31
  """
32
32
  subregionSize = pexConfig.ListField(
33
33
  dtype=int,
34
- doc=("width, height of stack subregion size; make "
34
+ doc=("Width and height of stack subregion size. Make the values "
35
35
  "small enough that a full stack of images will "
36
36
  "fit into memory at once."),
37
37
  length=2,
@@ -78,3 +78,43 @@ Examples of `ChoiceField` and `ConfigField` and the use of the `Config` object's
78
78
  if self.doComputeApCorr and not self.doPsf:
79
79
  raise ValueError("Cannot compute aperture correction "
80
80
  "without doing PSF determination.")
81
+
82
+ Examples of `~.configurableActions.ConfigurableActionField` and `~.configurableActions.ConfigurableActionStructField` making use of `~.configurableActions.ConfigurableAction`\ s in a `Config` object.
83
+
84
+ .. code-block:: python
85
+
86
+ class ExampleAction(pexConfig.configurableActions.ConfigurableAction):
87
+ """A ConfigurableAction that performs a simple calculation"""
88
+
89
+ numerator = pexConfig.Field[float](doc="Numerator for division operation")
90
+ divisor = pexConfig.Field[float](doc="Divisor for division operation")
91
+
92
+ def __call__(self, **kwargs):
93
+ return self.numerator / self.divisor
94
+
95
+
96
+ class ExampleConfig(pexConfig.Config):
97
+ """An example Config class which contains multiple `ConfigurableAction`\ s."""
98
+
99
+ divideAction = pexConfig.configurableActions.ConfigurableActionField(
100
+ doc="A field which points to a single action."
101
+ default=ExampleAction
102
+ )
103
+
104
+ multipleDivisionActions = pexConfig.configurableActions.ConfigurableActionStructField(
105
+ doc="A field which acts as a struct, referring to multiple ConfigurableActions."
106
+ )
107
+
108
+ def setDefaults(self):
109
+ """Example of setting multiple default configurations with `ConfigurableAction`\ s.
110
+ """
111
+ self.divideAction.numerator = 1
112
+ self.divideAction.divisor = 2
113
+
114
+ self.multipleDivisionActions.subDivide1 = ExampleAction()
115
+ self.multipleDivisionActions.subDivide1.numerator = 5
116
+ self.multipleDivisionActions.subDivide1.divisor = 10
117
+
118
+ self.multipleDivisionActions.subDivide2 = ExampleAction()
119
+ self.multipleDivisionActions.subDivide2.numerator = 7
120
+ self.multipleDivisionActions.subDivide2.divisor = 8
@@ -62,3 +62,7 @@ Python API reference
62
62
  .. automodapi:: lsst.pex.config.history
63
63
  :no-main-docstr:
64
64
  :no-inheritance-diagram:
65
+
66
+ .. automodapi:: lsst.pex.config.configurableActions
67
+ :no-main-docstr:
68
+ :no-inheritance-diagram:
@@ -8,7 +8,7 @@ Field access
8
8
  ------------
9
9
 
10
10
  Iterating through a `Config` instance yields the names of the `Field` attributes it contains.
11
- The `Config` class also supports many dictionary-like methods: `~Config.keys`, `~Config.items`, `~Config.iterkeys`, `~Config.iteritems`, and `~Config.itervalues`.
11
+ The `Config` class also supports many dictionary-like methods: `~Config.keys`, `~Config.items`, and `~Config.values`.
12
12
 
13
13
  History
14
14
  -------
@@ -49,7 +49,7 @@ Here is a config class that includes five fields:
49
49
  dtype=float,
50
50
  default=1.0)
51
51
  saturatedMaskName = pexConfig.Field(
52
- doc="Name of mask plane to use in saturation detection",
52
+ doc="Name of the mask plane to use in saturation detection.",
53
53
  dtype=str,
54
54
  default="SAT")
55
55
  flatScalingType = pexConfig.ChoiceField(
@@ -62,7 +62,7 @@ Here is a config class that includes five fields:
62
62
  "MEDIAN": "Scale by the inverse of the median",
63
63
  })
64
64
  keysToRemoveFromAssembledCcd = pexConfig.ListField(
65
- doc="fields to remove from the metadata of the assembled ccd.",
65
+ doc="Fields to remove from the metadata of the assembled ccd.",
66
66
  dtype=str,
67
67
  default=[])
68
68
 
@@ -167,3 +167,15 @@ Alternatively, if you wish to locate another configuration file using LSST infra
167
167
  from lsst.utils import getPackageDir
168
168
 
169
169
  config.load(os.path.join(getPackageDir("product_x"), "config", "otherconfig.py"))
170
+
171
+ Specialized Config subclasses
172
+ =============================
173
+
174
+ There exists a subclass of `Config` which is designed to be configurable like a standard config, but have a runtime call interface.
175
+ These specialized subclasses are named `~.configurableActions.ConfigurableAction`\ s, or actions for short.
176
+ These actions are not intended to replace other runtime components, but compliment them.
177
+ They provide configuration time mechanics to a simple runtime function.
178
+ This interface allows for both configuration of an action as well as making which action to run configurable.
179
+ These configurations are serialized out in a standard way, and thus allow complete functional states to be completely restored.
180
+ The selection (thus configuration) of which `~.configurableActions.ConfigurableAction`\ s to run is made possible through the use of special `Field`\ s named `~.configurableActions.ConfigurableActionField` and `~.configurableActions.ConfigurableActionStructField`.
181
+ See :doc:`field-types` for more details and examples of both `~.configurableActions.ConfigurableAction`\ s and the corresponding fields.
@@ -6,21 +6,21 @@ Registry and RegistryField for configuring subtasks
6
6
 
7
7
  Example of a `RegistryField` created from a `Registry` object and use of both the `Registry.register` method and the `registerConfigurable` decorator::
8
8
 
9
- psfDeterminerRegistry = pexConfig.makeRegistry("""A registry of PSF determiner factories""")
9
+ psfDeterminerRegistry = pexConfig.makeRegistry("""A registry of PSF determiner factories.""")
10
10
 
11
11
  class PcaPsfDeterminerConfig(pexConfig.Config):
12
12
  spatialOrder = pexConfig.Field(
13
- "spatial order for PSF kernel creation", int, 2)
13
+ "Spatial order for PSF kernel creation.", int, 2)
14
14
  [...]
15
15
 
16
16
  @pexConfig.registerConfigurable("pca", psfDeterminerRegistry)
17
17
  class PcaPsfDeterminer(object):
18
18
  ConfigClass = PcaPsfDeterminerConfig
19
- # associate this Configurable class with its Config class for use
20
- # by the registry
21
- def __init__(self, config, schema=None):
19
+ # Associate this Configurable class with its Config class for use
20
+ # by the registry.
21
+ def __init__(self, config, schema=None, **kwargs):
22
22
  [...]
23
- def determinePsf(self, exposure, psfCandidateList, metadata=None):
23
+ def determinePsf(self, exposure, psfCandidateList, metadata=None, **kwargs):
24
24
  [...]
25
25
 
26
26
  psfDeterminerRegistry.register("shapelet", ShapeletPsfDeterminer)
@@ -4,7 +4,7 @@
4
4
  Wrapping C++ control objects
5
5
  ############################
6
6
 
7
- C++ control objects defined using the ``LSST_CONTROL_FIELD`` macro in `lsst/pex/config.h` can be wrapped using pybind11 and the functions in `lsst.pex.config.wrap`, creating an equivalent Python `Config` object.
7
+ C++ control objects defined using the ``LSST_CONTROL_FIELD`` macro in ``lsst/pex/config.h`` can be wrapped using pybind11 and the functions in `lsst.pex.config.wrap`, creating an equivalent Python `Config` object.
8
8
  The `Config` will automatically create and set values in the C++ object, will provide access to the doc strings from C++, and will even call the C++ class's ``validate`` method, if one exists.
9
9
  This helps to minimize duplication of code.
10
10
 
@@ -0,0 +1,196 @@
1
+ [build-system]
2
+ requires = ["setuptools", "lsst-versions >= 1.3.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lsst-pex-config"
7
+ requires-python = ">=3.10.0"
8
+ description = "A flexible configuration system using Python files."
9
+ license = "BSD-3-Clause OR GPL-3.0-or-later"
10
+ license-files = ["COPYRIGHT", "LICENSE", "gpl-v3.0.txt", "bsd_license.txt"]
11
+ readme = "README.rst"
12
+ authors = [
13
+ {name="Rubin Observatory Data Management", email="dm-admin@lists.lsst.org"},
14
+ ]
15
+ classifiers = [
16
+ "Intended Audience :: Science/Research",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Scientific/Engineering :: Astronomy",
24
+ ]
25
+ keywords = ["lsst"]
26
+ dependencies = [
27
+ "pyyaml >=5.1",
28
+ "numpy >= 1.17",
29
+ ]
30
+ dynamic = ["version"]
31
+
32
+ [project.urls]
33
+ "Homepage" = "https://github.com/lsst/pex_config"
34
+
35
+ [project.optional-dependencies]
36
+ test = [
37
+ "pytest >= 3.2",
38
+ "pytest-openfiles >= 0.5.0",
39
+ ]
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["python"]
43
+
44
+ [tool.setuptools]
45
+ zip-safe = true
46
+
47
+ [tool.setuptools.package-data]
48
+ "lsst.pex.config" = ["py.typed"]
49
+
50
+ [tool.setuptools.dynamic]
51
+ version = { attr = "lsst_versions.get_lsst_version" }
52
+
53
+ [tool.black]
54
+ line-length = 110
55
+ target-version = ["py310"]
56
+
57
+ [tool.isort]
58
+ profile = "black"
59
+ line_length = 110
60
+ known_first_party = ["lsst"]
61
+
62
+ [tool.towncrier]
63
+ package = "lsst.pex.config"
64
+ package_dir = "python"
65
+ filename = "doc/lsst.pex.config/CHANGES.rst"
66
+ directory = "doc/changes"
67
+ title_format = "lsst-pex-config {version} ({project_date})"
68
+ issue_format = "`{issue} <https://rubinobs.atlassian.net/browse/{issue}>`_"
69
+
70
+ [[tool.towncrier.type]]
71
+ directory = "feature"
72
+ name = "New Features"
73
+ showcontent = true
74
+
75
+ [[tool.towncrier.type]]
76
+ directory = "api"
77
+ name = "API Changes"
78
+ showcontent = true
79
+
80
+ [[tool.towncrier.type]]
81
+ directory = "bugfix"
82
+ name = "Bug Fixes"
83
+ showcontent = true
84
+
85
+ [[tool.towncrier.type]]
86
+ directory = "perf"
87
+ name = "Performance Enhancement"
88
+ showcontent = true
89
+
90
+ [[tool.towncrier.type]]
91
+ directory = "misc"
92
+ name = "Other Changes and Additions"
93
+ showcontent = true
94
+
95
+ [[tool.towncrier.type]]
96
+ directory = "removal"
97
+ name = "An API Removal or Deprecation"
98
+ showcontent = true
99
+
100
+ [[tool.towncrier.type]]
101
+ directory = "doc"
102
+ name = "Documentation Enhancement"
103
+ showcontent = true
104
+
105
+ [tool.lsst_versions]
106
+ write_to = "python/lsst/pex/config/version.py"
107
+
108
+ [tool.pytest.ini_options]
109
+
110
+ [tool.pydocstyle]
111
+ convention = "numpy"
112
+ # Our coding style does not require docstrings for magic methods (D105)
113
+ # Our docstyle documents __init__ at the class level (D107)
114
+ # We allow methods to inherit docstrings and this is not compatible with D102.
115
+ # Docstring at the very first line is not required
116
+ # D200, D205 and D400 all complain if the first sentence of the docstring does
117
+ # not fit on one line.
118
+ add-ignore = ["D107", "D105", "D102", "D100", "D200", "D205", "D400", "D104"]
119
+
120
+ [tool.ruff]
121
+ line-length = 110
122
+ target-version = "py310"
123
+ exclude = [
124
+ "__init__.py",
125
+ "tests/config/*.py",
126
+ ]
127
+
128
+ [tool.ruff.lint]
129
+ ignore = [
130
+ "N802",
131
+ "N803",
132
+ "N806",
133
+ "N812",
134
+ "N815",
135
+ "N816",
136
+ "N999",
137
+ "D107",
138
+ "D105",
139
+ "D102",
140
+ "D104",
141
+ "D100",
142
+ "D200",
143
+ "D205",
144
+ ]
145
+ select = [
146
+ "E", # pycodestyle
147
+ "F", # pyflakes
148
+ "N", # pep8-naming
149
+ "W", # pycodestyle
150
+ "D", # pydocstyle
151
+ "UP", # pyupgrade
152
+ "I", # isort
153
+ "RUF022", # sort __all__
154
+ "C4", # comprehensions
155
+ "B", # bugbear
156
+ ]
157
+ extend-select = [
158
+ "RUF100", # Warn about unused noqa
159
+ ]
160
+
161
+ [tool.ruff.lint.per-file-ignores]
162
+ "tests/testLib.py" = ["F403", "F405"] # Wildcard imports.
163
+
164
+ [tool.ruff.lint.isort]
165
+ known-first-party = ["lsst"]
166
+
167
+ [tool.ruff.lint.pycodestyle]
168
+ max-doc-length = 79
169
+
170
+ [tool.ruff.lint.pydocstyle]
171
+ convention = "numpy"
172
+
173
+ [tool.ruff.format]
174
+ docstring-code-format = true
175
+ # Formatter does not know about indenting.
176
+ docstring-code-line-length = 69
177
+
178
+ [tool.numpydoc_validation]
179
+ checks = [
180
+ "all", # All except the rules listed below.
181
+ "SA01", # See Also section.
182
+ "EX01", # Example section.
183
+ "SS06", # Summary can go into second line.
184
+ "GL01", # Summary text can start on same line as """
185
+ "GL08", # Do not require docstring.
186
+ "ES01", # No extended summary required.
187
+ "RT01", # Unfortunately our @property trigger this.
188
+ "RT02", # Does not want named return value. DM style says we do.
189
+ "SS05", # pydocstyle is better at finding infinitive verb.
190
+ "SA04", # Do not require descriptions on all See Also entries.
191
+ ]
192
+ exclude = [
193
+ '^__init__$',
194
+ '\._[a-zA-Z_]+$', # Private methods.
195
+ "^test_.*", # Do not test docstrings in test code.
196
+ ]
@@ -25,7 +25,7 @@
25
25
  # You should have received a copy of the GNU General Public License
26
26
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
27
27
 
28
- __all__ = ["getCallerFrame", "getStackFrame", "StackFrame", "getCallStack"]
28
+ __all__ = ["StackFrame", "getCallStack", "getCallerFrame", "getStackFrame"]
29
29
 
30
30
  import inspect
31
31
  import linecache
@@ -42,7 +42,7 @@ def getCallerFrame(relative=0):
42
42
 
43
43
  Returns
44
44
  -------
45
- frame : `__builtin__.Frame`
45
+ frame : `types.FrameType`
46
46
  Frame for the caller.
47
47
 
48
48
  Notes
@@ -50,7 +50,7 @@ def getCallerFrame(relative=0):
50
50
  This function is excluded from the frame.
51
51
  """
52
52
  frame = inspect.currentframe().f_back.f_back # Our caller's caller
53
- for ii in range(relative):
53
+ for _ in range(relative):
54
54
  frame = frame.f_back
55
55
  return frame
56
56
 
@@ -97,7 +97,7 @@ class StackFrame:
97
97
  stack trace by the fact that it does not look up the source code until it
98
98
  is absolutely necessary, reducing the I/O.
99
99
 
100
- See also
100
+ See Also
101
101
  --------
102
102
  getStackFrame
103
103
  """
@@ -127,7 +127,7 @@ class StackFrame:
127
127
 
128
128
  Parameters
129
129
  ----------
130
- frame : `Frame`
130
+ frame : `types.FrameType`
131
131
  Frame object to interpret, such as from `inspect.currentframe`.
132
132
 
133
133
  Returns
@@ -149,7 +149,7 @@ class StackFrame:
149
149
  return cls(filename, lineno, function)
150
150
 
151
151
  def __repr__(self):
152
- return "%s(%s, %s, %s)" % (self.__class__.__name__, self.filename, self.lineno, self.function)
152
+ return f"{self.__class__.__name__}({self.filename}, {self.lineno}, {self.function})"
153
153
 
154
154
  def format(self, full=False):
155
155
  """Format for printing.
@@ -165,9 +165,9 @@ class StackFrame:
165
165
  result : `str`
166
166
  Formatted string.
167
167
  """
168
- result = " File %s:%s (%s)" % (self.filename, self.lineno, self.function)
168
+ result = f" File {self.filename}:{self.lineno} ({self.function})"
169
169
  if full:
170
- result += "\n %s" % (self.content,)
170
+ result += f"\n {self.content}"
171
171
  return result
172
172
 
173
173
 
@@ -56,7 +56,7 @@ class ChoiceField(Field[FieldTypeVar]):
56
56
  A description of why this Field is deprecated, including removal date.
57
57
  If not None, the string is appended to the docstring for this Field.
58
58
 
59
- See also
59
+ See Also
60
60
  --------
61
61
  ConfigChoiceField
62
62
  ConfigDictField
@@ -89,10 +89,11 @@ class ChoiceField(Field[FieldTypeVar]):
89
89
  for choice, choiceDoc in self.allowed.items():
90
90
  if choice is not None and not isinstance(choice, dtype):
91
91
  raise ValueError(
92
- "ChoiceField's allowed choice %s is of incorrect type %s. Expected %s"
93
- % (choice, _typeStr(choice), _typeStr(dtype))
92
+ f"ChoiceField's allowed choice {choice} is of incorrect type "
93
+ f"{_typeStr(choice)}. Expected {_typeStr(dtype)}"
94
94
  )
95
- self.__doc__ += "%s\n %s\n" % ("``{0!r}``".format(str(choice)), choiceDoc)
95
+ # Force to a string so that additional quotes are added with !r
96
+ self.__doc__ += f"``{str(choice)!r}``\n {choiceDoc}\n"
96
97
 
97
98
  self.source = getStackFrame()
98
99
 
@@ -32,7 +32,7 @@ or `lsst.pex.config.Field._compare` implementation, as they take care of
32
32
  writing messages as well as floating-point comparisons and shortcuts.
33
33
  """
34
34
 
35
- __all__ = ("getComparisonName", "compareScalars", "compareConfigs")
35
+ __all__ = ("compareConfigs", "compareScalars", "getComparisonName")
36
36
 
37
37
  import numpy
38
38
 
@@ -55,7 +55,7 @@ def getComparisonName(name1, name2):
55
55
  formatted as ``"{name1} / {name2}"``.
56
56
  """
57
57
  if name1 != name2:
58
- return "%s / %s" % (name1, name2)
58
+ return f"{name1} / {name2}"
59
59
  return name1
60
60
 
61
61
 
@@ -68,7 +68,8 @@ def compareScalars(name, v1, v2, output, rtol=1e-8, atol=1e-8, dtype=None):
68
68
  ----------
69
69
  name : `str`
70
70
  Name to use when reporting differences, typically created by
71
- `getComparisonName`.
71
+ `getComparisonName`. This will always appear as the beginning of any
72
+ messages reported via ``output``.
72
73
  v1 : object
73
74
  Left-hand side value to compare.
74
75
  v2 : object
@@ -89,7 +90,7 @@ def compareScalars(name, v1, v2, output, rtol=1e-8, atol=1e-8, dtype=None):
89
90
  areEqual : `bool`
90
91
  `True` if the values are equal, `False` if they are not.
91
92
 
92
- See also
93
+ See Also
93
94
  --------
94
95
  lsst.pex.config.compareConfigs
95
96
 
@@ -104,7 +105,7 @@ def compareScalars(name, v1, v2, output, rtol=1e-8, atol=1e-8, dtype=None):
104
105
  else:
105
106
  result = v1 == v2
106
107
  if not result and output is not None:
107
- output("Inequality in %s: %r != %r" % (name, v1, v2))
108
+ output(f"{name}: {v1!r} != {v2!r}")
108
109
  return result
109
110
 
110
111
 
@@ -117,10 +118,11 @@ def compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=Non
117
118
  ----------
118
119
  name : `str`
119
120
  Name to use when reporting differences, typically created by
120
- `getComparisonName`.
121
- v1 : `lsst.pex.config.Config`
121
+ `getComparisonName`. This will always appear as the beginning of any
122
+ messages reported via ``output``.
123
+ c1 : `lsst.pex.config.Config`
122
124
  Left-hand side config to compare.
123
- v2 : `lsst.pex.config.Config`
125
+ c2 : `lsst.pex.config.Config`
124
126
  Right-hand side config to compare.
125
127
  shortcut : `bool`, optional
126
128
  If `True`, return as soon as an inequality is found. Default is `True`.
@@ -138,7 +140,7 @@ def compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=Non
138
140
  `True` when the two `lsst.pex.config.Config` instances are equal.
139
141
  `False` if there is an inequality.
140
142
 
141
- See also
143
+ See Also
142
144
  --------
143
145
  lsst.pex.config.compareScalars
144
146
 
@@ -150,22 +152,24 @@ def compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=Non
150
152
  `~lsst.pex.config.ConfigChoiceField` instances, *unselected*
151
153
  `~lsst.pex.config.Config` instances will not be compared.
152
154
  """
155
+ from .config import _typeStr
156
+
153
157
  assert name is not None
154
158
  if c1 is None:
155
159
  if c2 is None:
156
160
  return True
157
161
  else:
158
162
  if output is not None:
159
- output("LHS is None for %s" % name)
163
+ output(f"{name}: None != {c2!r}.")
160
164
  return False
161
165
  else:
162
166
  if c2 is None:
163
167
  if output is not None:
164
- output("RHS is None for %s" % name)
168
+ output(f"{name}: {c1!r} != None.")
165
169
  return False
166
- if type(c1) != type(c2):
170
+ if type(c1) is not type(c2):
167
171
  if output is not None:
168
- output("Config types do not match for %s: %s != %s" % (name, type(c1), type(c2)))
172
+ output(f"{name}: config types do not match; {_typeStr(c1)} != {_typeStr(c2)}.")
169
173
  return False
170
174
  equal = True
171
175
  for field in c1._fields.values():