From: <pj...@us...> - 2008-06-18 20:01:38
|
Revision: 4666 http://jython.svn.sourceforge.net/jython/?rev=4666&view=rev Author: pjenvey Date: 2008-06-18 13:01:28 -0700 (Wed, 18 Jun 2008) Log Message: ----------- fix slotted objects always having a __dict__ which was broken in r4303. match the CPython slot behavior in test_slots_jy. and fix the test_descr slots tests Modified Paths: -------------- trunk/jython/Lib/test/test_descr.py trunk/jython/build.xml trunk/jython/src/org/python/core/PyType.java Added Paths: ----------- trunk/jython/Lib/test/test_slots_jy.py Modified: trunk/jython/Lib/test/test_descr.py =================================================================== --- trunk/jython/Lib/test/test_descr.py 2008-06-18 14:59:31 UTC (rev 4665) +++ trunk/jython/Lib/test/test_descr.py 2008-06-18 20:01:28 UTC (rev 4666) @@ -1251,7 +1251,10 @@ raise TestFailed, "[unichr(128)] slots not caught" # Test leaks - class Counted(object): + # XXX: Jython new style classes don't support __del__ + # http://bugs.jython.org/issue1057 + class Counted: + #class Counted(object): counter = 0 # counts the number of instances alive def __init__(self): Counted.counter += 1 @@ -1265,6 +1268,7 @@ x.c = Counted() vereq(Counted.counter, 3) del x + extra_collect() vereq(Counted.counter, 0) class D(C): pass @@ -1273,6 +1277,7 @@ x.z = Counted() vereq(Counted.counter, 2) del x + extra_collect() vereq(Counted.counter, 0) class E(D): __slots__ = ['e'] @@ -1282,6 +1287,7 @@ x.e = Counted() vereq(Counted.counter, 3) del x + extra_collect() vereq(Counted.counter, 0) # Test cyclical leaks [SF bug 519621] @@ -1294,8 +1300,13 @@ s = None import gc gc.collect() + extra_collect() vereq(Counted.counter, 0) + # XXX: This tests a CPython GC reference count bug and Jython lacks + # gc.get_objects + import sys + """ # Test lookup leaks [SF bug 572567] import sys,gc class G(object): @@ -1307,6 +1318,7 @@ g==g new_objects = len(gc.get_objects()) vereq(orig_objects, new_objects) + """ class H(object): __slots__ = ['a', 'b'] def __init__(self): @@ -1321,6 +1333,7 @@ h = H() try: del h + extra_collect() finally: sys.stderr = save_stderr @@ -1335,6 +1348,9 @@ a.foo = 42 vereq(a.__dict__, {"foo": 42}) + # XXX: Jython doesn't support __weakref__ + return + class W(object): __slots__ = ["__weakref__"] a = W() @@ -4388,10 +4404,6 @@ classmethods_in_c, staticmethods_in_c, - # pjenvey broke slots - slots, - slotspecials, - # Jython allows subclassing of classes it shouldn't (like # builtin_function_or_method): # http://bugs.jython.org/issue1758319 Added: trunk/jython/Lib/test/test_slots_jy.py =================================================================== --- trunk/jython/Lib/test/test_slots_jy.py (rev 0) +++ trunk/jython/Lib/test/test_slots_jy.py 2008-06-18 20:01:28 UTC (rev 4666) @@ -0,0 +1,133 @@ +"""Slot tests + +Made for Jython. +""" +import test_support +import unittest + +# The strict tests fail on PyPy (but work on CPython and Jython). +# They're questionable +strict = True + +class SlottedTestCase(unittest.TestCase): + + def test_slotted(self): + class Foo(object): + __slots__ = 'bar' + self.assert_('__dict__' not in Foo.__dict__) + foo = Foo() + self.assert_(not hasattr(foo, '__dict__')) + foo.bar = 'hello bar' + self.assertEqual(foo.bar, 'hello bar') + self.assertRaises(AttributeError, setattr, foo, 'foo', 'hello foo') + + class Baz(object): + __slots__ = ['python', 'jython'] + self.assert_('__dict__' not in Baz.__dict__) + baz = Baz() + self.assert_(not hasattr(baz, '__dict__')) + baz.python = 'hello python' + baz.jython = 'hello jython' + self.assertEqual(baz.python, 'hello python') + self.assertEqual(baz.jython, 'hello jython') + self.assertRaises(AttributeError, setattr, baz, 'foo', 'hello') + + +class SlottedWithDictTestCase(unittest.TestCase): + + def test_subclass(self): + class Base(object): + pass + class Foo(Base): + __slots__ = 'bar' + self.assert_('__dict__' not in Foo.__dict__) + foo = Foo() + self.assert_(hasattr(foo, '__dict__')) + foo.bar = 'hello bar' + foo.foo = 'hello foo' + self.assertEqual(foo.bar, 'hello bar') + self.assertEqual(foo.__dict__, {'foo': 'hello foo'}) + + def test_subclass_mro(self): + class Base(object): + pass + class Slotted(object): + __slots__ = 'baz' + class Foo(Slotted, Base): + __slots__ = 'bar' + if strict: + self.assert_('__dict__' in Foo.__dict__) + self.assertEqual(Foo.__dict__['__dict__'].__objclass__, Foo) + foo = Foo() + self.assert_(hasattr(foo, '__dict__')) + foo.bar = 'hello bar' + foo.baz = 'hello baz' + foo.foo = 'hello foo' + self.assertEqual(foo.bar, 'hello bar') + self.assertEqual(foo.baz, 'hello baz') + self.assertEqual(foo.__dict__, {'foo': 'hello foo'}) + + class Bar(Slotted, Base): + pass + if strict: + self.assert_('__dict__' in Bar.__dict__) + self.assertEqual(Bar.__dict__['__dict__'].__objclass__, Bar) + bar = Bar() + self.assert_(hasattr(bar, '__dict__')) + bar.bar = 'hello bar' + bar.baz = 'hello baz' + bar.foo = 'hello foo' + self.assertEqual(bar.bar, 'hello bar') + self.assertEqual(bar.baz, 'hello baz') + self.assertEqual(bar.__dict__, {'foo': 'hello foo', 'bar': 'hello bar'}) + + def test_subclass_oldstyle(self): + class OldBase: + pass + class Foo(OldBase, object): + __slots__ = 'bar' + if strict: + self.assert_('__dict__' in Foo.__dict__) + self.assertEqual(Foo.__dict__['__dict__'].__objclass__, Foo) + foo = Foo() + self.assert_(hasattr(foo, '__dict__')) + foo.bar = 'hello bar' + foo.foo = 'hello foo' + self.assertEqual(foo.bar, 'hello bar') + self.assertEqual(foo.__dict__, {'foo': 'hello foo'}) + + class Bar(OldBase, object): + __slots__ = '__dict__' + self.assert_('__dict__' in Bar.__dict__) + self.assertEqual(Bar.__dict__['__dict__'].__objclass__, Bar) + bar = Bar() + self.assert_(hasattr(bar, '__dict__')) + bar.bar = 'hello bar' + bar.foo = 'hello foo' + self.assertEqual(bar.bar, 'hello bar') + self.assertEqual(bar.__dict__, {'foo': 'hello foo', 'bar': 'hello bar'}) + + def test_mixin_oldstyle(self): + class OldBase: + pass + class NewBase(object): + pass + class Baz(NewBase, OldBase): + __slots__ = 'baz' + self.assert_('__dict__' not in Baz.__dict__) + baz = Baz() + self.assert_(hasattr(baz, '__dict__')) + baz.baz = 'hello baz' + baz.bar = 'hello bar' + self.assertEqual(baz.baz, 'hello baz') + self.assertEqual(baz.bar, 'hello bar') + self.assertEqual(baz.__dict__, {'bar': 'hello bar'}) + + +def test_main(): + test_support.run_unittest(SlottedTestCase, + SlottedWithDictTestCase) + + +if __name__ == '__main__': + test_main() Modified: trunk/jython/build.xml =================================================================== --- trunk/jython/build.xml 2008-06-18 14:59:31 UTC (rev 4665) +++ trunk/jython/build.xml 2008-06-18 20:01:28 UTC (rev 4666) @@ -728,8 +728,6 @@ </target> <target name="regrtest" depends="developer-build"> <exec executable="${dist.dir}/bin/jython"> - <!-- Temporarily give regrtest more memory until slots are fixed --> - <arg value="-J-Xmx128m"/> <arg value="${dist.dir}/Lib/test/regrtest.py"/> <!-- Only run the tests that are expected to work on Jython --> <arg value="--expected"/> Modified: trunk/jython/src/org/python/core/PyType.java =================================================================== --- trunk/jython/src/org/python/core/PyType.java 2008-06-18 14:59:31 UTC (rev 4665) +++ trunk/jython/src/org/python/core/PyType.java 2008-06-18 20:01:28 UTC (rev 4666) @@ -70,7 +70,7 @@ private boolean needs_finalizer; /** Whether this type's instances require a __dict__. */ - private boolean needs_userdict = true; + private boolean needs_userdict = false; /** The number of __slots__ defined. */ private int numSlots; @@ -172,8 +172,15 @@ newtype.bases = bases_list; PyObject slots = dict.__finditem__("__slots__"); - if (slots != null) { - newtype.needs_userdict = false; + boolean needsDictDescr = false; + if (slots == null) { + newtype.needs_userdict = true; + // a dict descriptor is required if base doesn't already provide a dict + needsDictDescr = !newtype.base.needs_userdict; + } else { + // have slots, but may inherit a dict + newtype.needs_userdict = newtype.base.needs_userdict; + if (slots instanceof PyString) { addSlot(newtype, slots); } else { @@ -181,10 +188,33 @@ addSlot(newtype, slotname); } } + + if (!newtype.base.needs_userdict && newtype.needs_userdict) { + // base doesn't provide dict but addSlot found the __dict__ slot + needsDictDescr = true; + } else if (bases_list.length > 0 && !newtype.needs_userdict) { + // secondary bases may provide dict + for (PyObject base : bases_list) { + if (base == newtype.base) { + // Skip primary base + continue; + } + if (base instanceof PyClass) { + // Classic base class provides dict + newtype.needs_userdict = true; + needsDictDescr = true; + break; + } + PyType tmpType = (PyType)base; + if (tmpType.needs_userdict) { + newtype.needs_userdict = true; + needsDictDescr = true; + // Nothing more to check + break; + } + } + } } - if (!newtype.needs_userdict) { - newtype.needs_userdict = necessitatesUserdict(bases_list); - } newtype.tp_flags = Py.TPFLAGS_HEAPTYPE; @@ -196,7 +226,7 @@ newtype.mro_internal(); // __dict__ descriptor - if (newtype.needs_userdict && newtype.lookup("__dict__") == null) { + if (needsDictDescr && dict.__finditem__("__dict__") == null) { dict.__setitem__("__dict__", new PyDataDescr(newtype, "__dict__", PyObject.class) { @Override @@ -832,17 +862,6 @@ return best; } - private static boolean necessitatesUserdict(PyObject[] bases_list) { - for (int i = 0; i < bases_list.length; i++) { - PyObject cur = bases_list[i]; - if ((cur instanceof PyType && ((PyType)cur).needs_userdict) - || cur instanceof PyClass) { - return true; - } - } - return false; - } - /** * Finds the most derived subtype of initialMetatype in the types * of bases, or initialMetatype if it is already the most derived. @@ -883,6 +902,9 @@ confirmIdentifier(slotname); String slotstring = mangleName(newtype.name, slotname.toString()); if (slotstring.equals("__dict__")) { + if (newtype.base.needs_userdict || newtype.needs_userdict) { + throw Py.TypeError("__dict__ slot disallowed: we already got one"); + } newtype.needs_userdict = true; } else if (newtype.dict.__finditem__(slotstring) == null) { newtype.dict.__setitem__(slotstring, new PySlot(newtype, slotstring, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |