[pypy-commit] pypy default: issue 2029: try a bit harder to hide the "default_factory" attribute

arigo noreply at buildbot.pypy.org
Tue Apr 14 13:01:38 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r76789:494280f37b17
Date: 2015-04-14 13:01 +0200
http://bitbucket.org/pypy/pypy/changeset/494280f37b17/

Log:	issue 2029: try a bit harder to hide the "default_factory" attribute

diff --git a/pypy/module/_collections/app_defaultdict.py b/pypy/module/_collections/app_defaultdict.py
--- a/pypy/module/_collections/app_defaultdict.py
+++ b/pypy/module/_collections/app_defaultdict.py
@@ -11,6 +11,7 @@
 
 
 class defaultdict(dict):
+    __slots__ = ['__default_factory']
 
     def __init__(self, *args, **kwds):
         if len(args) > 0:
@@ -20,7 +21,7 @@
                 raise TypeError("first argument must be callable")
         else:
             default_factory = None
-        self.default_factory = default_factory
+        defaultdict.__default_factory.__set__(self, default_factory)
         super(defaultdict, self).__init__(*args, **kwds)
 
     def __missing__(self, key):
@@ -33,15 +34,15 @@
             return "defaultdict(...)"
         try:
             recurse.add(id(self))
-            return "defaultdict(%s, %s)" % (repr(self.default_factory), super(defaultdict, self).__repr__())
+            return "defaultdict(%s, %s)" % (self.__default_factory,
+                                            super(defaultdict, self).__repr__())
         finally:
             recurse.remove(id(self))
 
     def copy(self):
-        return type(self)(self.default_factory, self)
+        return type(self)(self.__default_factory, self)
 
-    def __copy__(self):
-        return self.copy()
+    __copy__ = copy
 
     def __reduce__(self):
         """
@@ -55,4 +56,5 @@
 
            This API is used by pickle.py and copy.py.
         """
-        return (type(self), (self.default_factory,), None, None, self.iteritems())
+        return (type(self), (self.__default_factory,), None, None,
+                defaultdict.iteritems(self))
diff --git a/pypy/module/_collections/interp_defaultdict.py b/pypy/module/_collections/interp_defaultdict.py
--- a/pypy/module/_collections/interp_defaultdict.py
+++ b/pypy/module/_collections/interp_defaultdict.py
@@ -4,7 +4,8 @@
     # An interp-level version of this method.  This is mostly only
     # useful because it can be executed atomically in the presence of
     # threads.
-    w_default_factory = space.getattr(w_self, space.wrap('default_factory'))
+    w_default_factory = space.getattr(w_self,
+            space.wrap('_defaultdict__default_factory'))
     if space.is_w(w_default_factory, space.w_None):
         raise OperationError(space.w_KeyError, space.newtuple([w_key]))
     w_value = space.call_function(w_default_factory)
diff --git a/pypy/module/_collections/test/test_defaultdict.py b/pypy/module/_collections/test/test_defaultdict.py
--- a/pypy/module/_collections/test/test_defaultdict.py
+++ b/pypy/module/_collections/test/test_defaultdict.py
@@ -54,3 +54,17 @@
         assert len(d2) == 1
         assert d2[2] == 3
         assert d2[3] == 42
+
+    def test_no_dict(self):
+        import _collections
+        assert not hasattr(_collections.defaultdict(), '__dict__')
+
+    def test_no_setattr(self):
+        import _collections
+        class D(_collections.defaultdict):
+            def __setattr__(self, attr, name):
+                raise AssertionError
+        d = D(int)
+        assert d['5'] == 0
+        d['6'] += 3
+        assert d['6'] == 3


More information about the pypy-commit mailing list