diff --git a/borg/item.py b/borg/item.py index b0cbf12a3..05ac47a2c 100644 --- a/borg/item.py +++ b/borg/item.py @@ -17,6 +17,8 @@ class PropDict: """ VALID_KEYS = None # override with in child class + __slots__ = ("_dict", ) # avoid setting attributes not supported by properties + def __init__(self, data_dict=None, **kw): if data_dict is None: data = kw @@ -39,7 +41,7 @@ class PropDict: def as_dict(self): """return the internal dictionary""" - return self._dict # XXX use StableDict? + return StableDict(self._dict) def _check_key(self, key): """make sure key is of type str and known""" @@ -108,6 +110,8 @@ class Item(PropDict): VALID_KEYS = set(key.decode() for key in ITEM_KEYS) # we want str-typed keys + __slots__ = ("_dict", ) # avoid setting attributes not supported by properties + # properties statically defined, so that IDEs can know their names: path = PropDict._make_property('path', str, 'surrogate-escaped str', encode=safe_encode, decode=safe_decode) diff --git a/borg/testsuite/item.py b/borg/testsuite/item.py index f8b6b5117..bd66e4831 100644 --- a/borg/testsuite/item.py +++ b/borg/testsuite/item.py @@ -136,3 +136,12 @@ def test_item_dict_property(): item.xattrs['bar'] = 'baz' assert item.xattrs == StableDict({'foo': 'bar', 'bar': 'baz'}) assert item.as_dict() == {'xattrs': {'foo': 'bar', 'bar': 'baz'}} + + +def test_unknown_property(): + # we do not want the user to be able to set unknown attributes - + # they won't get into the .as_dict() result dictionary. + # also they might be just typos of known attributes. + item = Item() + with pytest.raises(AttributeError): + item.unknown_attribute = None