[Jython-checkins] jython: Generalize adding special slot descriptors (__dict__, __weakref__)
jim.baker
jython-checkins at python.org
Tue Mar 10 18:10:39 CET 2015
https://hg.python.org/jython/rev/4d28c47997de
changeset: 7607:4d28c47997de
user: Jim Baker <jim.baker at rackspace.com>
date: Tue Mar 10 11:10:34 2015 -0600
summary:
Generalize adding special slot descriptors (__dict__, __weakref__)
Jython used to be too strict, and would throw TypeError if the special
slot descriptors of __dict__ and __weakref__ were used in a
subclass. With this change, CPython is stricter than Jython (see tests
in test_slots_jy for specifics). However Jython's underlying object
model is based on Java, and that give us more flexibility. In
particular, it's now possible to add a __dict__ slot descriptor to a
subclass of Java classes, such as java.util.HashMap.
Fixes http://bugs.jython.org/issue2272 and adds support for Werkzeug
files:
Lib/test/test_slots_jy.py | 98 ++++++++++++++++++++-
src/org/python/core/PyType.java | 34 ++++--
2 files changed, 120 insertions(+), 12 deletions(-)
diff --git a/Lib/test/test_slots_jy.py b/Lib/test/test_slots_jy.py
--- a/Lib/test/test_slots_jy.py
+++ b/Lib/test/test_slots_jy.py
@@ -3,6 +3,7 @@
Made for Jython.
"""
from test import test_support
+from java.util import HashMap
import unittest
# The strict tests fail on PyPy (but work on CPython and Jython).
@@ -134,10 +135,105 @@
self.assert_(hasattr(Foo, '__weakref__'))
+class SpecialSlotsBaseTestCase(unittest.TestCase):
+
+ # Tests for http://bugs.jython.org/issue2272, including support for
+ # werkzeug.local.LocalProxy
+
+ def make_class(self, base, slot):
+ class C(base):
+ __slots__ = (slot)
+ if slot == "__dict__":
+ @property
+ def __dict__(self):
+ return {"x": 42, "y": 47}
+ def __getattr__(self, name):
+ try:
+ return self.__dict__[name]
+ except KeyError:
+ raise AttributeError("%r object has no attribute %r" % (
+ self.__class__.__name__, name))
+ return C
+
+ def test_dict_slot(self):
+ C = self.make_class(object, "__dict__")
+ c = C()
+ self.assertIn("__dict__", dir(c))
+ self.assertIn("x", dir(c))
+ self.assertIn("y", dir(c))
+ self.assertEqual(c.__dict__.get("x"), 42)
+ self.assertEqual(c.x, 42)
+ self.assertEqual(c.y, 47)
+ with self.assertRaisesRegexp(AttributeError, r"'C' object has no attribute 'z'"):
+ c.z
+
+ def test_dict_slot_str(self):
+ # Unlike CPython, Jython does not arbitrarily limit adding
+ # __dict__ slot to str and other types that are not object
+ C = self.make_class(str, "__dict__")
+ c = C("abc123")
+ self.assertTrue(c.startswith("abc"))
+ self.assertIn("__dict__", dir(c))
+ self.assertIn("x", dir(c))
+ self.assertIn("y", dir(c))
+ self.assertEqual(c.__dict__.get("x"), 42)
+ self.assertEqual(c.x, 42)
+ self.assertEqual(c.y, 47)
+ with self.assertRaisesRegexp(AttributeError, r"'C' object has no attribute 'z'"):
+ c.z
+
+ def test_dict_slot_subclass(self):
+ # Unlike CPython, Jython does not arbitrarily limit adding __dict__ slot to subtypes
+ class B(object):
+ @property
+ def w(self):
+ return 23
+ C = self.make_class(B, "__dict__")
+ c = C()
+ self.assertIn("__dict__", dir(c))
+ self.assertIn("x", dir(c))
+ self.assertIn("y", dir(c))
+ self.assertEqual(c.__dict__.get("x"), 42)
+ self.assertEqual(c.x, 42)
+ self.assertEqual(c.y, 47)
+ with self.assertRaisesRegexp(AttributeError, r"'C' object has no attribute 'z'"):
+ c.z
+ self.assertEqual(c.w, 23)
+
+ def test_dict_slot_subclass_java_hashmap(self):
+ C = self.make_class(HashMap, "__dict__")
+ # has everything in a HashMap, including Python semantic equivalence
+ c = C({"a": 1, "b": 2})
+ self.assertTrue(c.containsKey("a"))
+ self.assertEqual(sorted(c.iteritems()), [("a", 1), ("b", 2)])
+ # but also has a __dict__ slot for further interesting ;) possibilities
+ self.assertIn("__dict__", dir(c))
+ self.assertIn("x", dir(c))
+ self.assertIn("y", dir(c))
+ self.assertEqual(c.__dict__.get("x"), 42)
+ self.assertEqual(c.x, 42)
+ self.assertEqual(c.y, 47)
+ with self.assertRaisesRegexp(AttributeError, r"'C' object has no attribute 'z'"):
+ c.z
+
+ def test_weakref_slot(self):
+ self.assertNotIn("__weakref__", dir(object()))
+ self.assertIn("__weakref__", dir(self.make_class(object, "__weakref__")()))
+ class B(object):
+ pass
+ self.assertIn("__weakref__", dir(B()))
+ self.assertIn("__weakref__", dir(self.make_class(B, "__weakref__")()))
+ self.assertNotIn("__weakref__", dir("abc"))
+ self.assertIn("__weakref__", dir(self.make_class(str, "__weakref__")()))
+ self.assertNotIn("__weakref__", dir(HashMap()))
+ self.assertIn("__weakref__", dir(self.make_class(HashMap, "__weakref__")()))
+
+
def test_main():
test_support.run_unittest(SlottedTestCase,
SlottedWithDictTestCase,
- SlottedWithWeakrefTestCase)
+ SlottedWithWeakrefTestCase,
+ SpecialSlotsBaseTestCase)
if __name__ == '__main__':
diff --git a/src/org/python/core/PyType.java b/src/org/python/core/PyType.java
--- a/src/org/python/core/PyType.java
+++ b/src/org/python/core/PyType.java
@@ -102,7 +102,7 @@
/** Mapping of Java classes to their PyTypes. */
private static Map<Class<?>, PyType> class_to_type;
private static Set<PyType> exposedTypes;
-
+
/** Mapping of Java classes to their TypeBuilders. */
private static Map<Class<?>, TypeBuilder> classToBuilder;
@@ -255,19 +255,31 @@
if (slotName.equals("__dict__")) {
if (!mayAddDict || wantDict) {
- throw Py.TypeError("__dict__ slot disallowed: we already got one");
+ // CPython is stricter here, but this seems arbitrary. To reproduce CPython
+ // behavior
+ // if (base != PyObject.TYPE) {
+ // throw Py.TypeError("__dict__ slot disallowed: we already got one");
+ // }
+ } else {
+ wantDict = true;
+ continue;
}
- wantDict = true;
} else if (slotName.equals("__weakref__")) {
- if (!mayAddWeak || wantWeak) {
- throw Py.TypeError("__weakref__ slot disallowed: we already got one");
+ if ((!mayAddWeak || wantWeak) && base != PyObject.TYPE) {
+ // CPython is stricter here, but this seems arbitrary. To reproduce CPython
+ // behavior
+ // if (base != PyObject.TYPE) {
+ // throw Py.TypeError("__weakref__ slot disallowed: we already got one");
+ // }
+ } else {
+ wantWeak = true;
+ continue;
}
- wantWeak = true;
- } else {
- slotName = mangleName(name, slotName);
- if (dict.__finditem__(slotName) == null) {
- dict.__setitem__(slotName, new PySlot(this, slotName, numSlots++));
- }
+ }
+
+ slotName = mangleName(name, slotName);
+ if (dict.__finditem__(slotName) == null) {
+ dict.__setitem__(slotName, new PySlot(this, slotName, numSlots++));
}
}
--
Repository URL: https://hg.python.org/jython
More information about the Jython-checkins
mailing list