Skip to content

Commit b3bb96d

Browse files
committed
Make AttributeDict additive
._read_only attribute is now a dict of name -> msgs Overrides .update() method to use __setitem__ implementation Includes updates to unit tests
1 parent 9acc204 commit b3bb96d

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

dash/_utils.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,23 +137,37 @@ def __getattr__(self, key):
137137
raise AttributeError(key)
138138

139139
def set_read_only(self, names, msg="Attribute is read-only"):
140-
object.__setattr__(self, "_read_only", names)
141-
object.__setattr__(self, "_read_only_msg", msg)
140+
"""
141+
Designate named attributes as read-only with the corresponding msg
142+
143+
Method is additive. Making additional calls to this method will update
144+
existing messages and add to the current set of _read_only names.
145+
"""
146+
new_read_only = {name: msg for name in names}
147+
if getattr(self, "_read_only", False):
148+
self._read_only.update(new_read_only)
149+
else:
150+
object.__setattr__(self, "_read_only", new_read_only)
142151

143152
def finalize(self, msg="Object is final: No new keys may be added."):
144153
"""Prevent any new keys being set."""
145154
object.__setattr__(self, "_final", msg)
146155

147156
def __setitem__(self, key, val):
148-
if key in self.__dict__.get("_read_only", []):
149-
raise AttributeError(self._read_only_msg, key)
157+
if key in self.__dict__.get("_read_only", {}):
158+
raise AttributeError(self._read_only[key], key)
150159

151160
final_msg = self.__dict__.get("_final")
152161
if final_msg and key not in self:
153162
raise AttributeError(final_msg, key)
154163

155164
return super().__setitem__(key, val)
156165

166+
def update(self, other):
167+
# Overrides dict.update() to use __setitem__ above
168+
for k, v in other.items():
169+
self[k] = v
170+
157171
# pylint: disable=inconsistent-return-statements
158172
def first(self, *names):
159173
for name in names:

tests/unit/dash/test_utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ def test_ddut001_attribute_dict():
2424
assert a.k == 2
2525
assert a["k"] == 2
2626

27-
a.set_read_only(["k", "q"], "boo")
27+
a.set_read_only(["k"], "boo")
2828

2929
with pytest.raises(AttributeError) as err:
3030
a.k = 3
3131
assert err.value.args == ("boo", "k")
3232
assert a.k == 2
33+
assert a._read_only == {"k": "boo"}
3334

3435
with pytest.raises(AttributeError) as err:
3536
a["k"] = 3
@@ -38,13 +39,11 @@ def test_ddut001_attribute_dict():
3839

3940
a.set_read_only(["q"])
4041

41-
a.k = 3
42-
assert a.k == 3
43-
4442
with pytest.raises(AttributeError) as err:
4543
a.q = 3
4644
assert err.value.args == ("Attribute is read-only", "q")
4745
assert "q" not in a
46+
assert a._read_only == {"k": "boo", "q": "Attribute is read-only"}
4847

4948
a.finalize("nope")
5049

0 commit comments

Comments
 (0)