Skip to content

Commit

Permalink
only expose _fields to the user, so deleted Fields remain hidden
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-simpson authored and timj committed Nov 5, 2024
1 parent 803eb36 commit 9966481
Showing 1 changed file with 34 additions and 12 deletions.
46 changes: 34 additions & 12 deletions python/lsst/pex/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,12 @@ def __set__(
if instance._frozen:
raise FieldValidationError(self, instance, "Cannot modify a frozen Config")

if at is None:
at = getCallStack()
# setDefaults() gets a free pass due to our mashing of inheritance
if self.name not in instance._fields:
raise AttributeError(f"{instance.__class__.__name__} has no attribute {self.name}")

history = instance._history.setdefault(self.name, [])
if value is not None:
value = _autocast(value, self.dtype)
Expand Down Expand Up @@ -996,6 +1002,9 @@ class behavior.
_history: dict[str, list[Any]]
_imports: set[Any]

# Only _fields are exposure. _storage retains items that have been
# deleted.

def __iter__(self):
"""Iterate over fields."""
return self._fields.__iter__()
Expand All @@ -1008,7 +1017,7 @@ def keys(self):
names : `~collections.abc.KeysView`
List of `lsst.pex.config.Field` names.
"""
return self._storage.keys()
return list(self._fields)

def values(self):
"""Get field values.
Expand All @@ -1018,7 +1027,7 @@ def values(self):
values : `~collections.abc.ValuesView`
Iterator of field values.
"""
return self._storage.values()
return self.toDict().values()

def items(self):
"""Get configurations as ``(field name, field value)`` pairs.
Expand All @@ -1031,7 +1040,11 @@ def items(self):
0. Field name.
1. Field value.
"""
return self._storage.items()
return self.toDict().items()

def doc(self, field):
"""Return docstring for field."""
return self._fields[field].doc

def __contains__(self, name):
"""Return `True` if the specified field exists in this config.
Expand All @@ -1046,7 +1059,7 @@ def __contains__(self, name):
in : `bool`
`True` if the specified field exists in the config.
"""
return self._storage.__contains__(name)
return self._storage.__contains__(name) and name in self._fields

def __new__(cls, *args, **kw):
"""Allocate a new `lsst.pex.config.Config` object.
Expand All @@ -1072,9 +1085,7 @@ def __new__(cls, *args, **kw):
instance._history = {}
instance._imports = set()
# load up defaults
for field in instance._fields.values():
instance._history[field.name] = []
field.__set__(instance, field.default, at=at + [field.source], label="default")
instance.reset(at=at)
# set custom default-overrides
instance.setDefaults()
# set constructor overrides
Expand All @@ -1094,6 +1105,14 @@ def __reduce__(self):
self.saveToStream(stream)
return (unreduceConfig, (self.__class__, stream.getvalue().encode()))

def reset(self, at=None):
"""Reset all values to their defaults."""
if at is None:
at = getCallStack()
for field in self._fields.values():
self._history[field.name] = []
field.__set__(self, field.default, at=at + [field.source], label="default")

def setDefaults(self):
"""Subclass hook for computing defaults.
Expand Down Expand Up @@ -1165,7 +1184,9 @@ def update(self, **kw):
field = self._fields[name]
field.__set__(self, value, at=at, label=label)
except KeyError:
raise KeyError(f"No field of name {name} exists in config type {_typeStr(self)}")
raise KeyError(
"{} has no field named {}".format(type(self).__name__.replace("Config", ""), name)
)

def load(self, filename, root="config"):
"""Modify this config in place by executing the Python code in a
Expand Down Expand Up @@ -1611,11 +1632,12 @@ def __setattr__(self, attr, value, at=None, label="assignment"):
raise AttributeError(f"{_typeStr(self)} has no attribute {attr}")

def __delattr__(self, attr, at=None, label="deletion"):
# CJS: Hacked to allow setDefaults() to delete non-existent fields
if at is None:
at = getCallStack()
if attr in self._fields:
if at is None:
at = getCallStack()
self._fields[attr].__delete__(self, at=at, label=label)
else:
del self._fields[attr]
elif not any(stk.function == "setDefaults" for stk in at):
object.__delattr__(self, attr)

def __eq__(self, other):
Expand Down

0 comments on commit 9966481

Please sign in to comment.