tbm / test

0 stars 0 forks source link

beancount.core.data.AttrDict.__getattr__ raises KeyError, foiling deepcopy. #79

Closed tbm closed 9 years ago

tbm commented 9 years ago

Original report by Nathan Grigg (Bitbucket: nathangrigg, GitHub: nathangrigg).


On an AttrDict instance (or anything containing one, e.g. a Posting), copy.deepcopy fails.

The reason is that deepcopy checks for a __deepcopy__ method at each stage, and accessing AttrDict.__deepcopy__ raises a KeyError (and deepcopy expects an AttributeError).

(The reason I'm using deepcopy is that I'm splitting a posting into two pieces, and I don't want their position or meta fields to be incorrectly linked.)

Example fix:

#!diff

--- a/src/python/beancount/core/data.py Sat Nov 21 17:13:39 2015 -0500
+++ b/src/python/beancount/core/data.py Sat Nov 21 15:14:17 2015 -0800
@@ -262,9 +262,14 @@
 class AttrDict(dict):
     """A dict with attribute access.
     """
-    __getattr__ = dict.__getitem__
     __setattr__ = dict.__setitem__

+    def __getattr__(self, key):
+        try:
+            return self[key]
+        except KeyError as err:
+            raise AttributeError(*err.args)
+
     def __getstate__(self):
         "Ensure this object may be pickled."
         return self
tbm commented 9 years ago

Original comment by Gary Peck (Bitbucket: tylix, GitHub: garyp).


You could also implement AttrDict like this to avoid having to override __getattr__ at all (though note the couple potential cons mentioned at http://stackoverflow.com/a/14620633):

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self
tbm commented 9 years ago

Original comment by Martin Blais (Bitbucket: blais, GitHub: blais).


I'd love to get rid of AttrDict at some point. The only reason for the overrides is so that it can be pickled for the load cache.

tbm commented 9 years ago

Original comment by Martin Blais (Bitbucket: blais, GitHub: blais).


I removed AttrDict. Metadata is now represented as a plain old dict. https://bitbucket.org/blais/beancount/src/1f029cd07e5c77c2b7e5e09b998471ae086131de/CHANGES?at=default&fileviewer=file-view-default#CHANGES-7 (Make sure to delete all .picklecache files if you've been using the load cache.)

Thanks again for reporting this problem.

tbm commented 9 years ago

Original comment by Martin Blais (Bitbucket: blais, GitHub: blais).


https://bitbucket.org/blais/beancount/src/1f029cd07e5c77c2b7e5e09b998471ae086131de/CHANGES?at=default&fileviewer=file-view-default#CHANGES-7