lsst-pex-config 30.0.0rc2__tar.gz → 30.0.1__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 (70) hide show
  1. {lsst_pex_config-30.0.0rc2/python/lsst_pex_config.egg-info → lsst_pex_config-30.0.1}/PKG-INFO +5 -3
  2. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/CHANGES.rst +23 -0
  3. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/pyproject.toml +4 -2
  4. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/callStack.py +1 -1
  5. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/choiceField.py +3 -3
  6. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/comparison.py +5 -5
  7. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/config.py +130 -37
  8. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configChoiceField.py +4 -4
  9. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configDictField.py +6 -6
  10. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configField.py +6 -6
  11. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configurableActions/_configurableActionStructField.py +1 -1
  12. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configurableField.py +7 -7
  13. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/dictField.py +6 -6
  14. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/listField.py +10 -10
  15. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/rangeField.py +2 -2
  16. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/registry.py +4 -4
  17. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/version.py +1 -1
  18. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/wrap.py +4 -4
  19. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1/python/lsst_pex_config.egg-info}/PKG-INFO +5 -3
  20. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst_pex_config.egg-info/requires.txt +1 -0
  21. lsst_pex_config-30.0.1/tests/test__file__.py +103 -0
  22. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_configChoiceField.py +14 -0
  23. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_configDictField.py +13 -0
  24. lsst_pex_config-30.0.0rc2/tests/test__file__.py +0 -55
  25. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/COPYRIGHT +0 -0
  26. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/LICENSE +0 -0
  27. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/MANIFEST.in +0 -0
  28. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/README.rst +0 -0
  29. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/bsd_license.txt +0 -0
  30. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/design-notes.rst +0 -0
  31. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/field-types.rst +0 -0
  32. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/index.rst +0 -0
  33. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/inspecting-configs.rst +0 -0
  34. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/overview.rst +0 -0
  35. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/registry-intro.rst +0 -0
  36. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/doc/lsst.pex.config/wrapping-cpp-control-objects.rst +0 -0
  37. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/gpl-v3.0.txt +0 -0
  38. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/__init__.py +0 -0
  39. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/__init__.py +0 -0
  40. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/__init__.py +0 -0
  41. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/_doNotImportMe.py +0 -0
  42. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configurableActions/__init__.py +0 -0
  43. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configurableActions/_configurableAction.py +0 -0
  44. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configurableActions/_configurableActionField.py +0 -0
  45. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/configurableActions/tests.py +0 -0
  46. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/convert.py +0 -0
  47. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/history.py +0 -0
  48. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst/pex/config/py.typed +0 -0
  49. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst_pex_config.egg-info/SOURCES.txt +0 -0
  50. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst_pex_config.egg-info/dependency_links.txt +0 -0
  51. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst_pex_config.egg-info/top_level.txt +0 -0
  52. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/python/lsst_pex_config.egg-info/zip-safe +0 -0
  53. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/setup.cfg +0 -0
  54. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/testLib.py +0 -0
  55. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_Config.py +0 -0
  56. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_configurableActions.py +0 -0
  57. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_configurableField.py +0 -0
  58. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_dictField.py +0 -0
  59. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_history.py +0 -0
  60. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_listField.py +0 -0
  61. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_registry.py +0 -0
  62. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_ticket1911.py +0 -0
  63. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_ticket1914.py +0 -0
  64. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_ticket1915.py +0 -0
  65. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_ticket1929.py +0 -0
  66. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_ticket1995.py +0 -0
  67. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_ticket2818.py +0 -0
  68. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_ticketDM-7337.py +0 -0
  69. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_unloaded_yaml.py +0 -0
  70. {lsst_pex_config-30.0.0rc2 → lsst_pex_config-30.0.1}/tests/test_wrap.py +0 -0
@@ -1,20 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-pex-config
3
- Version: 30.0.0rc2
3
+ Version: 30.0.1
4
4
  Summary: A flexible configuration system using Python files.
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License-Expression: BSD-3-Clause OR GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/lsst/pex_config
8
+ Project-URL: Source, https://github.com/lsst/pex_config
8
9
  Keywords: lsst
9
10
  Classifier: Intended Audience :: Science/Research
10
11
  Classifier: Operating System :: OS Independent
11
12
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.14
13
14
  Classifier: Programming Language :: Python :: 3.11
14
15
  Classifier: Programming Language :: Python :: 3.12
15
16
  Classifier: Programming Language :: Python :: 3.13
16
17
  Classifier: Topic :: Scientific/Engineering :: Astronomy
17
- Requires-Python: >=3.10.0
18
+ Requires-Python: >=3.11.0
18
19
  Description-Content-Type: text/x-rst
19
20
  License-File: COPYRIGHT
20
21
  License-File: LICENSE
@@ -22,6 +23,7 @@ License-File: gpl-v3.0.txt
22
23
  License-File: bsd_license.txt
23
24
  Requires-Dist: pyyaml>=5.1
24
25
  Requires-Dist: numpy>=1.17
26
+ Requires-Dist: lsst-resources
25
27
  Provides-Extra: test
26
28
  Requires-Dist: pytest>=3.2; extra == "test"
27
29
  Requires-Dist: pytest-openfiles>=0.5.0; extra == "test"
@@ -1,3 +1,26 @@
1
+ lsst-pex-config v30.0.0 (2026-01-15)
2
+ ====================================
3
+
4
+ Python 3.11 is now the minimum supported version.
5
+ Tested on Python 3.14.
6
+
7
+ New Features
8
+ ------------
9
+
10
+ - Added a ``copy`` method that unfreezes. (`DM-16523 <https://rubinobs.atlassian.net/browse/DM-16523>`_)
11
+ - * Changed the config loader method such that if a relative path is specified inside another config it is treated as being relative to that original config file.
12
+ * Modified the config loader to support URI schemes ``file``, ``resource``, and ``eups`` supported by ``lsst-resources``.
13
+ This change means that in some cases ``__file__`` can be a URI string and not a simple path. (`DM-33226 <https://rubinobs.atlassian.net/browse/DM-33226>`_)
14
+
15
+
16
+ Bug Fixes
17
+ ---------
18
+
19
+ - Fixed a bug that caused history-printing to fail when the initial value for a field was `None`. (`DM-51850 <https://rubinobs.atlassian.net/browse/DM-51850>`_)
20
+ - Fixed copying of ``ConfigDictField``. (`DM-53767 <https://rubinobs.atlassian.net/browse/DM-53767>`_)
21
+ - Fixed copying of ``ConfigChoiceField`` and ``RegistryField``. (`DM-53791 <https://rubinobs.atlassian.net/browse/DM-53791>`_)
22
+
23
+
1
24
  lsst-pex-config v29.0.0 (2025-03-25)
2
25
  ====================================
3
26
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "lsst-pex-config"
7
- requires-python = ">=3.10.0"
7
+ requires-python = ">=3.11.0"
8
8
  description = "A flexible configuration system using Python files."
9
9
  license = "BSD-3-Clause OR GPL-3.0-or-later"
10
10
  license-files = ["COPYRIGHT", "LICENSE", "gpl-v3.0.txt", "bsd_license.txt"]
@@ -16,7 +16,7 @@ classifiers = [
16
16
  "Intended Audience :: Science/Research",
17
17
  "Operating System :: OS Independent",
18
18
  "Programming Language :: Python :: 3",
19
- "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.14",
20
20
  "Programming Language :: Python :: 3.11",
21
21
  "Programming Language :: Python :: 3.12",
22
22
  "Programming Language :: Python :: 3.13",
@@ -26,11 +26,13 @@ keywords = ["lsst"]
26
26
  dependencies = [
27
27
  "pyyaml >=5.1",
28
28
  "numpy >= 1.17",
29
+ "lsst-resources",
29
30
  ]
30
31
  dynamic = ["version"]
31
32
 
32
33
  [project.urls]
33
34
  "Homepage" = "https://github.com/lsst/pex_config"
35
+ "Source" = "https://github.com/lsst/pex_config"
34
36
 
35
37
  [project.optional-dependencies]
36
38
  test = [
@@ -102,7 +102,7 @@ class StackFrame:
102
102
  getStackFrame
103
103
  """
104
104
 
105
- _STRIP = "/python/lsst/"
105
+ _STRIP = "/python/"
106
106
  """String to strip from the ``filename`` in the constructor."""
107
107
 
108
108
  def __init__(self, filename, lineno, function, content=None):
@@ -42,7 +42,7 @@ class ChoiceField(Field[FieldTypeVar]):
42
42
  ----------
43
43
  doc : `str`
44
44
  Documentation string that describes the configuration field.
45
- dtype : class
45
+ dtype : `type`
46
46
  The type of the field's choices. For example, `str` or `int`.
47
47
  allowed : `dict`
48
48
  The allowed values. Keys are the allowed choices (of ``dtype``-type).
@@ -52,9 +52,9 @@ class ChoiceField(Field[FieldTypeVar]):
52
52
  choices.
53
53
  optional : `bool`, optional
54
54
  If `True`, this configuration field is *optional*. Default is `True`.
55
- deprecated : None or `str`, optional
55
+ deprecated : `None` or `str`, optional
56
56
  A description of why this Field is deprecated, including removal date.
57
- If not None, the string is appended to the docstring for this Field.
57
+ If not `None`, the string is appended to the docstring for this Field.
58
58
 
59
59
  See Also
60
60
  --------
@@ -70,18 +70,18 @@ def compareScalars(name, v1, v2, output, rtol=1e-8, atol=1e-8, dtype=None):
70
70
  Name to use when reporting differences, typically created by
71
71
  `getComparisonName`. This will always appear as the beginning of any
72
72
  messages reported via ``output``.
73
- v1 : object
73
+ v1 : `object`
74
74
  Left-hand side value to compare.
75
- v2 : object
75
+ v2 : `object`
76
76
  Right-hand side value to compare.
77
- output : callable or `None`
77
+ output : `collections.abc.Callable` or `None`
78
78
  A callable that takes a string, used (possibly repeatedly) to report
79
79
  inequalities (for example, `print`). Set to `None` to disable output.
80
80
  rtol : `float`, optional
81
81
  Relative tolerance for floating point comparisons.
82
82
  atol : `float`, optional
83
83
  Absolute tolerance for floating point comparisons.
84
- dtype : class, optional
84
+ dtype : `type`, optional
85
85
  Data type of values for comparison. May be `None` if values are not
86
86
  floating-point.
87
87
 
@@ -130,7 +130,7 @@ def compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=Non
130
130
  Relative tolerance for floating point comparisons.
131
131
  atol : `float`, optional
132
132
  Absolute tolerance for floating point comparisons.
133
- output : callable, optional
133
+ output : `collections.abc.Callable`, optional
134
134
  A callable that takes a string, used (possibly repeatedly) to report
135
135
  inequalities. For example: `print`.
136
136
 
@@ -38,6 +38,7 @@ __all__ = (
38
38
  import copy
39
39
  import importlib
40
40
  import io
41
+ import logging
41
42
  import math
42
43
  import numbers
43
44
  import os
@@ -47,9 +48,13 @@ import sys
47
48
  import tempfile
48
49
  import warnings
49
50
  from collections.abc import Mapping
51
+ from contextlib import contextmanager
52
+ from contextvars import ContextVar
50
53
  from types import GenericAlias
51
54
  from typing import Any, ForwardRef, Generic, TypeVar, cast, overload
52
55
 
56
+ from lsst.resources import ResourcePath, ResourcePathExpression
57
+
53
58
  # if YAML is not available that's fine and we simply don't register
54
59
  # the yaml representer since we know it won't be used.
55
60
  try:
@@ -74,6 +79,25 @@ else:
74
79
  YamlLoaders = ()
75
80
  doImport = None
76
81
 
82
+ _LOG = logging.getLogger(__name__)
83
+
84
+
85
+ # Tracks the current config directory for the current context.
86
+ _config_dir_stack: ContextVar[ResourcePath | None] = ContextVar("_config_dir_stack", default=None)
87
+
88
+
89
+ def _get_config_root() -> ResourcePath | None:
90
+ return _config_dir_stack.get()
91
+
92
+
93
+ @contextmanager
94
+ def _push_config_root(dirname: ResourcePath):
95
+ token = _config_dir_stack.set(dirname)
96
+ try:
97
+ yield
98
+ finally:
99
+ _config_dir_stack.reset(token)
100
+
77
101
 
78
102
  class _PexConfigGenericAlias(GenericAlias):
79
103
  """A Subclass of python's GenericAlias used in defining and instantiating
@@ -124,14 +148,14 @@ def _autocast(x, dtype):
124
148
 
125
149
  Parameters
126
150
  ----------
127
- x : object
151
+ x : `object`
128
152
  A value.
129
153
  dtype : type
130
154
  Data type, such as `float`, `int`, or `str`.
131
155
 
132
156
  Returns
133
157
  -------
134
- values : object
158
+ values : `object`
135
159
  If appropriate, the returned value is ``x`` cast to the given type
136
160
  ``dtype``. If the cast cannot be performed the original value of
137
161
  ``x`` is returned.
@@ -276,7 +300,7 @@ class FieldValidationError(ValueError):
276
300
 
277
301
  See also
278
302
  --------
279
- lsst.pex.config.Field.name
303
+ ``lsst.pex.config.Field.name``
280
304
  """
281
305
 
282
306
  self.fullname = _joinNamePath(config._name, field.name)
@@ -297,7 +321,7 @@ class FieldValidationError(ValueError):
297
321
  error = (
298
322
  f"{self.fieldType.__name__} '{self.fullname}' failed validation: {msg}\n"
299
323
  f"For more information see the Field definition at:\n{self.fieldSource.format()}"
300
- f" and the Config definition at:\n{self.configSource.format()}"
324
+ f" and the Config definition for {_typeStr(config)} at:\n{self.configSource.format()}"
301
325
  )
302
326
  super().__init__(error)
303
327
 
@@ -310,14 +334,14 @@ class Field(Generic[FieldTypeVar]):
310
334
  ----------
311
335
  doc : `str`
312
336
  A description of the field for users.
313
- dtype : type, optional
337
+ dtype : `type`, optional
314
338
  The field's data type. ``Field`` only supports basic data types:
315
339
  `int`, `float`, `complex`, `bool`, and `str`. See
316
340
  `Field.supportedTypes`. Optional if supplied as a typing argument to
317
341
  the class.
318
- default : object, optional
342
+ default : `object`, optional
319
343
  The field's default value.
320
- check : callable, optional
344
+ check : `collections.abc.Callable`, optional
321
345
  A callable that is called with the field's value. This callable should
322
346
  return `False` if the value is invalid. More complex inter-field
323
347
  validation can be written as part of the
@@ -326,7 +350,7 @@ class Field(Generic[FieldTypeVar]):
326
350
  This sets whether the field is considered optional, and therefore
327
351
  doesn't need to be set by the user. When `False`,
328
352
  `lsst.pex.config.Config.validate` fails if the field's value is `None`.
329
- deprecated : None or `str`, optional
353
+ deprecated : `None` or `str`, optional
330
354
  A description of why this Field is deprecated, including removal date.
331
355
  If not None, the string is appended to the docstring for this Field.
332
356
 
@@ -350,7 +374,7 @@ class Field(Generic[FieldTypeVar]):
350
374
 
351
375
  Notes
352
376
  -----
353
- ``Field`` instances (including those of any subclass of ``Field``) are used
377
+ `Field` instances (including those of any subclass of `Field`) are used
354
378
  as class attributes of `~lsst.pex.config.Config` subclasses (see the
355
379
  example, below). ``Field`` attributes work like the `property` attributes
356
380
  of classes that implement custom setters and getters. `Field` attributes
@@ -361,15 +385,15 @@ class Field(Generic[FieldTypeVar]):
361
385
  When you access a `Field` attribute on a `Config` instance, you don't
362
386
  get the `Field` instance itself. Instead, you get the value of that field,
363
387
  which might be a simple type (`int`, `float`, `str`, `bool`) or a custom
364
- container type (like a `lsst.pex.config.List`) depending on the field's
365
- type. See the example, below.
388
+ container type (like a `lsst.pex.config.ListField`) depending on the
389
+ field's type. See the example, below.
366
390
 
367
391
  Fields can be annotated with a type similar to other python classes (python
368
392
  specification `here <https://peps.python.org/pep-0484/#generics>`_ ).
369
393
  See the name field in the Config example below for an example of this.
370
394
  Unlike most other uses in python, this has an effect at type checking *and*
371
395
  runtime. If the type is specified with a class annotation, it will be used
372
- as the value of the ``dtype`` in the ``Field`` and there is no need to
396
+ as the value of the ``dtype`` in the `Field` and there is no need to
373
397
  specify it as an argument during instantiation.
374
398
 
375
399
  There are Some notes on dtype through type annotation syntax. Type
@@ -377,19 +401,20 @@ class Field(Generic[FieldTypeVar]):
377
401
  name. i.e. "float", but this cannot be used to resolve circular references.
378
402
  Type annotation syntax can be used on an identifier in addition to Class
379
403
  assignment i.e. ``variable: Field[str] = Config.someField`` vs
380
- ``someField = Field[str](doc="some doc"). However, this syntax is only
404
+ ``someField = Field[str](doc="some doc")``. However, this syntax is only
381
405
  useful for annotating the type of the identifier (i.e. variable in previous
382
- example) and does nothing for assigning the dtype of the ``Field``.
406
+ example) and does nothing for assigning the dtype of the `Field`.
383
407
 
384
408
  Examples
385
409
  --------
386
- Instances of ``Field`` should be used as class attributes of
410
+ Instances of `Field` should be used as class attributes of
387
411
  `lsst.pex.config.Config` subclasses:
388
412
 
389
413
  >>> from lsst.pex.config import Config, Field
390
414
  >>> class Example(Config):
391
415
  ... myInt = Field("An integer field.", int, default=0)
392
416
  ... name = Field[str](doc="A string Field")
417
+ >>> config = Example()
393
418
  >>> print(config.myInt)
394
419
  0
395
420
  >>> config.myInt = 5
@@ -600,7 +625,7 @@ class Field(Generic[FieldTypeVar]):
600
625
 
601
626
  Parameters
602
627
  ----------
603
- value : object
628
+ value : `object`
604
629
  The value being validated.
605
630
 
606
631
  Raises
@@ -642,8 +667,8 @@ class Field(Generic[FieldTypeVar]):
642
667
 
643
668
  Parameters
644
669
  ----------
645
- outfile : file-like object
646
- A writeable field handle.
670
+ outfile : `typing.IO`
671
+ A writeable file handle.
647
672
  instance : `~lsst.pex.config.Config`
648
673
  The `~lsst.pex.config.Config` instance that contains this field.
649
674
 
@@ -684,7 +709,7 @@ class Field(Generic[FieldTypeVar]):
684
709
 
685
710
  Returns
686
711
  -------
687
- value : object
712
+ value : `object`
688
713
  The field's value. See *Notes*.
689
714
 
690
715
  Notes
@@ -843,7 +868,7 @@ class Field(Generic[FieldTypeVar]):
843
868
  Relative tolerance for floating point comparisons.
844
869
  atol : `float`, optional
845
870
  Absolute tolerance for floating point comparisons.
846
- output : callable, optional
871
+ output : `collections.abc.Callable`, optional
847
872
  A callable that takes a string, used (possibly repeatedly) to
848
873
  report inequalities.
849
874
 
@@ -1171,15 +1196,51 @@ class Config(metaclass=ConfigMeta): # type: ignore
1171
1196
  e.add_note(f"No field of name {name} exists in config type {_typeStr(self)}")
1172
1197
  raise
1173
1198
 
1199
+ def _filename_to_resource(
1200
+ self, filename: ResourcePathExpression | None = None
1201
+ ) -> tuple[ResourcePath | None, str]:
1202
+ """Create resource path from filename.
1203
+
1204
+ Parameters
1205
+ ----------
1206
+ filename : `lsst.resources.ResourcePathExpression` or `None`
1207
+ The URI expression associated with this config. Can be `None`
1208
+ if no file URI is known.
1209
+
1210
+ Returns
1211
+ -------
1212
+ resource : `lsst.resources.ResourcePath` or `None`
1213
+ The resource version of the filename. Returns `None` if no filename
1214
+ was given or refers to unspecified value.
1215
+ file_string : `str`
1216
+ String form of the resource for use in ``__file__``
1217
+ """
1218
+ if filename is None or filename in ("?", "<unknown>"):
1219
+ return None, "<unknown>"
1220
+ base = _get_config_root()
1221
+ resource = ResourcePath(filename, forceAbsolute=True, forceDirectory=False, root=base)
1222
+
1223
+ # Preferred definition of __file__ is the full OS path. If a config
1224
+ # is loaded with a relative path it must be converted to the absolute
1225
+ # path to avoid confusion with later relative paths referenced inside
1226
+ # the config.
1227
+ if resource.scheme == "file":
1228
+ file_string = resource.ospath
1229
+ else:
1230
+ file_string = str(resource)
1231
+
1232
+ return resource, file_string
1233
+
1174
1234
  def load(self, filename, root="config"):
1175
1235
  """Modify this config in place by executing the Python code in a
1176
1236
  configuration file.
1177
1237
 
1178
1238
  Parameters
1179
1239
  ----------
1180
- filename : `str`
1181
- Name of the configuration file. A configuration file is Python
1182
- module.
1240
+ filename : `lsst.resources.ResourcePathExpression`
1241
+ Name of the configuration URI. A configuration file is a Python
1242
+ module. Since configuration files are Python code, remote URIs
1243
+ are not allowed.
1183
1244
  root : `str`, optional
1184
1245
  Name of the variable in file that refers to the config being
1185
1246
  overridden.
@@ -1199,9 +1260,21 @@ class Config(metaclass=ConfigMeta): # type: ignore
1199
1260
  lsst.pex.config.Config.saveToStream
1200
1261
  lsst.pex.config.Config.saveToString
1201
1262
  """
1202
- with open(filename) as f:
1203
- code = compile(f.read(), filename=filename, mode="exec")
1204
- self.loadFromString(code, root=root, filename=filename)
1263
+ resource, file_string = self._filename_to_resource(filename)
1264
+ if resource is None:
1265
+ # A filename is required.
1266
+ raise ValueError(f"Undefined URI provided to load command: {filename}.")
1267
+
1268
+ if resource.scheme not in ("file", "eups", "resource"):
1269
+ raise ValueError(f"Remote URI ({resource}) can not be used to load configurations.")
1270
+
1271
+ # Push the directory of the file we are now reading onto the stack
1272
+ # so that nested loads are relative to this file.
1273
+ with _push_config_root(resource.dirname()):
1274
+ _LOG.debug("Updating config from URI %s", str(resource))
1275
+ with resource.open("r") as f:
1276
+ code = compile(f.read(), filename=file_string, mode="exec")
1277
+ self._loadFromString(code, root=root, filename=file_string)
1205
1278
 
1206
1279
  def loadFromStream(self, stream, root="config", filename=None, extraLocals=None):
1207
1280
  """Modify this Config in place by executing the Python code in the
@@ -1209,7 +1282,7 @@ class Config(metaclass=ConfigMeta): # type: ignore
1209
1282
 
1210
1283
  Parameters
1211
1284
  ----------
1212
- stream : file-like object, `str`, `bytes`, or `~types.CodeType`
1285
+ stream : `typing.IO`, `str`, `bytes`, or `~types.CodeType`
1213
1286
  Stream containing configuration override code. If this is a
1214
1287
  code object, it should be compiled with ``mode="exec"``.
1215
1288
  root : `str`, optional
@@ -1224,7 +1297,8 @@ class Config(metaclass=ConfigMeta): # type: ignore
1224
1297
  Then this config's field ``myField`` is set to ``5``.
1225
1298
  filename : `str`, optional
1226
1299
  Name of the configuration file, or `None` if unknown or contained
1227
- in the stream. Used for error reporting.
1300
+ in the stream. Used for error reporting and to set ``__file__``
1301
+ variable in config.
1228
1302
  extraLocals : `dict` of `str` to `object`, optional
1229
1303
  Any extra variables to include in local scope when loading.
1230
1304
 
@@ -1244,7 +1318,7 @@ class Config(metaclass=ConfigMeta): # type: ignore
1244
1318
  """
1245
1319
  if hasattr(stream, "read"):
1246
1320
  if filename is None:
1247
- filename = getattr(stream, "name", "?")
1321
+ filename = getattr(stream, "name", "<unknown>")
1248
1322
  code = compile(stream.read(), filename=filename, mode="exec")
1249
1323
  else:
1250
1324
  code = stream
@@ -1268,9 +1342,11 @@ class Config(metaclass=ConfigMeta): # type: ignore
1268
1342
  config.myField = 5
1269
1343
 
1270
1344
  Then this config's field ``myField`` is set to ``5``.
1271
- filename : `str`, optional
1272
- Name of the configuration file, or `None` if unknown or contained
1273
- in the stream. Used for error reporting.
1345
+ filename : `lsst.resources.ResourcePathExpression`, optional
1346
+ URI of the configuration file, or `None` if unknown or contained
1347
+ in the stream. Used for error reporting and to set ``__file__``
1348
+ variable. Required to be set if the string config attempts to
1349
+ load other configs using either relative path or ``__file__``.
1274
1350
  extraLocals : `dict` of `str` to `object`, optional
1275
1351
  Any extra variables to include in local scope when loading.
1276
1352
 
@@ -1291,7 +1367,24 @@ class Config(metaclass=ConfigMeta): # type: ignore
1291
1367
  if filename is None:
1292
1368
  # try to determine the file name; a compiled string
1293
1369
  # has attribute "co_filename",
1294
- filename = getattr(code, "co_filename", "?")
1370
+ filename = getattr(code, "co_filename", "<unknown>")
1371
+
1372
+ resource, file_string = self._filename_to_resource(filename)
1373
+ if resource is None:
1374
+ # No idea where this config came from so no ability to deal with
1375
+ # relative paths. No reason to use context.
1376
+ self._loadFromString(code, root=root, filename=filename, extraLocals=extraLocals)
1377
+ else:
1378
+ # Push the directory of the file we are now reading onto the stack
1379
+ # so that nested loads are relative to this file.
1380
+ with _push_config_root(resource.dirname()):
1381
+ self._loadFromString(code, root=root, filename=file_string, extraLocals=extraLocals)
1382
+
1383
+ def _loadFromString(self, code, root="config", filename=None, extraLocals=None):
1384
+ """Update config from string.
1385
+
1386
+ Assumes relative directory path context has been setup by caller.
1387
+ """
1295
1388
  with RecordingImporter() as importer:
1296
1389
  globals = {"__file__": filename}
1297
1390
  local = {root: self}
@@ -1374,7 +1467,7 @@ class Config(metaclass=ConfigMeta): # type: ignore
1374
1467
 
1375
1468
  Parameters
1376
1469
  ----------
1377
- outfile : file-like object
1470
+ outfile : `typing.TextIO`
1378
1471
  Destination file object write the config into. Accepts strings not
1379
1472
  bytes.
1380
1473
  root : `str`, optional
@@ -1428,7 +1521,7 @@ class Config(metaclass=ConfigMeta): # type: ignore
1428
1521
 
1429
1522
  Parameters
1430
1523
  ----------
1431
- outfile : file-like object
1524
+ outfile : `typing.TextIO`
1432
1525
  Destination file object write the config into. Accepts strings not
1433
1526
  bytes.
1434
1527
  """
@@ -1663,7 +1756,7 @@ class Config(metaclass=ConfigMeta): # type: ignore
1663
1756
  Relative tolerance for floating point comparisons.
1664
1757
  atol : `float`, optional
1665
1758
  Absolute tolerance for floating point comparisons.
1666
- output : callable, optional
1759
+ output : `collections.abc.Callable`, optional
1667
1760
  A callable that takes a string, used (possibly repeatedly) to
1668
1761
  report inequalities.
1669
1762
 
@@ -1784,7 +1877,7 @@ def unreduceConfig(cls_, stream):
1784
1877
  cls_ : `lsst.pex.config.Config`-type
1785
1878
  A `lsst.pex.config.Config` type (not an instance) that is instantiated
1786
1879
  with configurations in the ``stream``.
1787
- stream : file-like object, `str`, or `~types.CodeType`
1880
+ stream : `typing.IO`, `str`, or `~types.CodeType`
1788
1881
  Stream containing configuration override code.
1789
1882
 
1790
1883
  Returns
@@ -192,7 +192,7 @@ class ConfigInstanceDict(collections.abc.Mapping[str, Config]):
192
192
  result._typemap = self._typemap
193
193
  if self._selection is not None:
194
194
  if self._field.multi:
195
- result._selection = SelectionSet(result._dict, self._selection._set)
195
+ result._selection = SelectionSet(self, self._selection._set)
196
196
  else:
197
197
  result._selection = self._selection
198
198
  return result
@@ -412,9 +412,9 @@ class ConfigChoiceField(Field[ConfigInstanceDict]):
412
412
  If `False`, the field allows only a single selection. In this case,
413
413
  set the active config by assigning the config's key from the
414
414
  ``typemap`` to the field's ``name`` attribute (see *Examples*).
415
- deprecated : None or `str`, optional
415
+ deprecated : `None` or `str`, optional
416
416
  A description of why this Field is deprecated, including removal date.
417
- If not None, the string is appended to the docstring for this Field.
417
+ If not `None`, the string is appended to the docstring for this Field.
418
418
 
419
419
  See Also
420
420
  --------
@@ -650,7 +650,7 @@ class ConfigChoiceField(Field[ConfigInstanceDict]):
650
650
  Relative tolerance for floating point comparisons.
651
651
  atol : `float`
652
652
  Absolute tolerance for floating point comparisons.
653
- output : callable
653
+ output : `collections.abc.Callable`
654
654
  A callable that takes a string, used (possibly repeatedly) to
655
655
  report inequalities.
656
656
 
@@ -76,7 +76,7 @@ class ConfigDict(Dict[str, Config]):
76
76
  return type(self)(
77
77
  config,
78
78
  self._field,
79
- {k: v._copy() for k, v in self._dict.items()},
79
+ {k: v.copy() for k, v in self._dict.items()},
80
80
  at=None,
81
81
  label="copy",
82
82
  setHistory=False,
@@ -148,7 +148,7 @@ class ConfigDictField(DictField):
148
148
  itemtype : `lsst.pex.config.Config`-type
149
149
  The type of the values in the mapping. This must be
150
150
  `~lsst.pex.config.Config` or a subclass.
151
- default : optional
151
+ default : `typing.Any`, optional
152
152
  Unknown.
153
153
  default : ``itemtype``-dtype, optional
154
154
  Default value of this field.
@@ -161,9 +161,9 @@ class ConfigDictField(DictField):
161
161
  Callable to check a key.
162
162
  itemCheck : `~collections.abc.Callable` or `None`, optional
163
163
  Callable to check an item.
164
- deprecated : None or `str`, optional
164
+ deprecated : `None` or `str`, optional
165
165
  A description of why this Field is deprecated, including removal date.
166
- If not None, the string is appended to the docstring for this Field.
166
+ If not `None`, the string is appended to the docstring for this Field.
167
167
 
168
168
  Raises
169
169
  ------
@@ -171,7 +171,7 @@ class ConfigDictField(DictField):
171
171
  Raised if the inputs are invalid:
172
172
 
173
173
  - ``keytype`` or ``itemtype`` arguments are not supported types
174
- (members of `ConfigDictField.supportedTypes`.
174
+ (members of `Field.supportedTypes`.
175
175
  - ``dictCheck``, ``keyCheck`` or ``itemCheck`` is not a callable
176
176
  function.
177
177
 
@@ -326,7 +326,7 @@ class ConfigDictField(DictField):
326
326
  Relative tolerance for floating point comparisons.
327
327
  atol : `float`
328
328
  Absolute tolerance for floating point comparisons.
329
- output : callable
329
+ output : `collections.abc.Callable`
330
330
  A callable that takes a string, used (possibly repeatedly) to
331
331
  report inequalities.
332
332
 
@@ -52,12 +52,12 @@ class ConfigField(Field[FieldTypeVar]):
52
52
  considered equivalent to assigning a default-constructed sub-config.
53
53
  This means that the argument default can be ``dtype``, as well as an
54
54
  instance of ``dtype``.
55
- check : callable, optional
55
+ check : `collections.abc.Callable`, optional
56
56
  A callback function that validates the field's value, returning `True`
57
57
  if the value is valid, and `False` otherwise.
58
- deprecated : None or `str`, optional
58
+ deprecated : `None` or `str`, optional
59
59
  A description of why this Field is deprecated, including removal date.
60
- If not None, the string is appended to the docstring for this Field.
60
+ If not `None`, the string is appended to the docstring for this Field.
61
61
 
62
62
  See Also
63
63
  --------
@@ -178,7 +178,7 @@ class ConfigField(Field[FieldTypeVar]):
178
178
 
179
179
  Parameters
180
180
  ----------
181
- outfile : file-like object
181
+ outfile : `typing.IO`
182
182
  A writeable field handle.
183
183
  instance : `~lsst.pex.config.Config`
184
184
  The `~lsst.pex.config.Config` instance that contains this field.
@@ -226,7 +226,7 @@ class ConfigField(Field[FieldTypeVar]):
226
226
 
227
227
  Returns
228
228
  -------
229
- value : object
229
+ value : `object`
230
230
  The field's value. See *Notes*.
231
231
 
232
232
  Notes
@@ -299,7 +299,7 @@ class ConfigField(Field[FieldTypeVar]):
299
299
  Relative tolerance for floating point comparisons.
300
300
  atol : `float`
301
301
  Absolute tolerance for floating point comparisons.
302
- output : callable
302
+ output : `collections.abc.Callable`
303
303
  A callable that takes a string, used (possibly repeatedly) to
304
304
  report inequalities.
305
305
 
@@ -446,7 +446,7 @@ class ConfigurableActionStructField(Field[ActionTypeVar]):
446
446
  Relative tolerance for floating point comparisons.
447
447
  atol : `float`
448
448
  Absolute tolerance for floating point comparisons.
449
- output : callable
449
+ output : `collections.abc.Callable`
450
450
  A callable that takes a string, used (possibly repeatedly) to
451
451
  report inequalities.
452
452