Skip to content

Commit eb35cdb

Browse files
chris-simpsontimj
authored andcommitted
only expose _fields to the user, so deleted Fields remain hidden
1 parent 385e435 commit eb35cdb

1 file changed

Lines changed: 49 additions & 12 deletions

File tree

python/lsst/pex/config/config.py

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,12 @@ def __set__(
818818
if instance._frozen:
819819
raise FieldValidationError(self, instance, "Cannot modify a frozen Config")
820820

821+
if at is None:
822+
at = getCallStack()
823+
# setDefaults() gets a free pass due to our mashing of inheritance
824+
if self.name not in instance._fields:
825+
raise AttributeError(f"{instance.__class__.__name__} has no attribute {self.name}")
826+
821827
history = instance._history.setdefault(self.name, [])
822828
if value is not None:
823829
value = _autocast(value, self.dtype)
@@ -1004,6 +1010,9 @@ class behavior.
10041010
_history: dict[str, list[Any]]
10051011
_imports: set[Any]
10061012

1013+
# Only _fields are exposure. _storage retains items that have been
1014+
# deleted.
1015+
10071016
def __iter__(self):
10081017
"""Iterate over fields."""
10091018
return self._fields.__iter__()
@@ -1016,7 +1025,7 @@ def keys(self):
10161025
names : `~collections.abc.KeysView`
10171026
List of `lsst.pex.config.Field` names.
10181027
"""
1019-
return self._storage.keys()
1028+
return list(self._fields)
10201029

10211030
def values(self):
10221031
"""Get field values.
@@ -1026,7 +1035,7 @@ def values(self):
10261035
values : `~collections.abc.ValuesView`
10271036
Iterator of field values.
10281037
"""
1029-
return self._storage.values()
1038+
return self.toDict().values()
10301039

10311040
def items(self):
10321041
"""Get configurations as ``(field name, field value)`` pairs.
@@ -1039,7 +1048,22 @@ def items(self):
10391048
0. Field name.
10401049
1. Field value.
10411050
"""
1042-
return self._storage.items()
1051+
return self.toDict().items()
1052+
1053+
def doc(self, field):
1054+
"""Return docstring for field.
1055+
1056+
Parameters
1057+
----------
1058+
field : `str`
1059+
Field to select.
1060+
1061+
Returns
1062+
-------
1063+
doc : `str`
1064+
Associated docstring.
1065+
"""
1066+
return self._fields[field].doc
10431067

10441068
def __contains__(self, name):
10451069
"""Return `True` if the specified field exists in this config.
@@ -1054,7 +1078,7 @@ def __contains__(self, name):
10541078
in : `bool`
10551079
`True` if the specified field exists in the config.
10561080
"""
1057-
return self._storage.__contains__(name)
1081+
return self._storage.__contains__(name) and name in self._fields
10581082

10591083
def __new__(cls, *args, **kw):
10601084
"""Allocate a new `lsst.pex.config.Config` object.
@@ -1080,9 +1104,7 @@ def __new__(cls, *args, **kw):
10801104
instance._history = {}
10811105
instance._imports = set()
10821106
# load up defaults
1083-
for field in instance._fields.values():
1084-
instance._history[field.name] = []
1085-
field.__set__(instance, field.default, at=at + [field.source], label="default")
1107+
instance.reset(at=at)
10861108
# set custom default-overrides
10871109
instance.setDefaults()
10881110
# set constructor overrides
@@ -1102,6 +1124,20 @@ def __reduce__(self):
11021124
self.saveToStream(stream)
11031125
return (unreduceConfig, (self.__class__, stream.getvalue().encode()))
11041126

1127+
def reset(self, at=None):
1128+
"""Reset all values to their defaults.
1129+
1130+
Parameters
1131+
----------
1132+
at : `lists` [ `StackFrame` ] or `None`, optional
1133+
Location in stack.
1134+
"""
1135+
if at is None:
1136+
at = getCallStack()
1137+
for field in self._fields.values():
1138+
self._history[field.name] = []
1139+
field.__set__(self, field.default, at=at + [field.source], label="default")
1140+
11051141
def setDefaults(self):
11061142
"""Subclass hook for computing defaults.
11071143
@@ -1170,7 +1206,7 @@ def update(self, **kw):
11701206
field = self._fields[name]
11711207
field.__set__(self, value, at=at, label=label)
11721208
except KeyError as e:
1173-
e.add_note(f"No field of name {name} exists in config type {_typeStr(self)}")
1209+
e.add_note(f"{type(self).__name__.replace('Config', '')} has no field named {name}")
11741210
raise
11751211

11761212
def load(self, filename, root="config"):
@@ -1617,11 +1653,12 @@ def __setattr__(self, attr, value, at=None, label="assignment"):
16171653
raise AttributeError(f"{_typeStr(self)} has no attribute {attr}")
16181654

16191655
def __delattr__(self, attr, at=None, label="deletion"):
1656+
# CJS: Hacked to allow setDefaults() to delete non-existent fields
1657+
if at is None:
1658+
at = getCallStack()
16201659
if attr in self._fields:
1621-
if at is None:
1622-
at = getCallStack()
1623-
self._fields[attr].__delete__(self, at=at, label=label)
1624-
else:
1660+
del self._fields[attr]
1661+
elif not any(stk.function == "setDefaults" for stk in at):
16251662
object.__delattr__(self, attr)
16261663

16271664
def __eq__(self, other):

0 commit comments

Comments
 (0)