test_repr (very) recently started failing for me, on Windows, but only when doing a full run of the test suite. Same symptom in release or debug builds. test_repr does not fail when run alone, and regardless of whether run directly from cmdline, or via "regrtest test_repr". Here's the failure: test_repr test test_repr failed -- Traceback (most recent call last): File "../lib/test\test_repr.py", line 156, in test_descriptors self.failUnless(repr(x).startswith('<staticmethod object at 0x')) File "C:\CODE\PYTHON\lib\unittest.py", line 256, in failUnless if not expr: raise self.failureException, msg AssertionError This is the code that's failing: class C: def foo(cls): pass x = staticmethod(C.foo) self.failUnless(repr(x).startswith('<staticmethod object at 0x')) By putting import sys; print >> sys.stderr, '*' * 30, repr(x) before the last line, when it fails repr(x) is actually: <staticmethod instance at 0x017E26C0> when it fails (although the address varies, of course). How can it be either "an object" or "an instance" depending on how the test is run? A staticmethod doesn't contain enough data that it's possible to forget to initialize any of it <0.7 wink>.
[Tim, reports test_repr failure due to repr(a_static_method), but only when the whole test suite is run, not in isolation] + The same thing happens with classmethod (repr is normally <classmethod object at 0xnnnnnnnn> but magically turns into <classmethod instance at 0xnnnnnnnn> "sometimes"). + The gross cause is that tp_repr is normally NULL in the classmethod and staticmethod types, and PyObject_Repr then prints "object at". But by the time test_repr starts running (but not in isolation), their tp_repr slots point to object_repr (which prints "instance at"). The tp_str and tp_hash slots have also changed. + To provoke a failure via regrtest, it's necessary and sufficient to run test_inspect before test_repr, in the same run: C:\Code\python\PCbuild>python ../lib/test/regrtest.py test_repr # ok test_repr 1 test OK. C:\Code\python\PCbuild>python ../lib/test/regrtest.py test_inspect test_repr # fails test_inspect test_repr test test_repr failed -- Traceback (most recent call last): File "../lib/test\test_repr.py", line 156, in test_descriptors self.failUnless(repr(x).startswith('<staticmethod object at 0x')) File "C:\CODE\PYTHON\lib\unittest.py", line 256, in failUnless if not expr: raise self.failureException, msg AssertionError 1 test OK. 1 test failed: test_repr C:\Code\python\PCbuild> + Turns out I provoked this by adding classify_class_attrs() to inspect.py, and then foolishly added a test for it <wink>. The symptom can be provoked by one judiciously chosen line: C:\Code\python\PCbuild>python Python 2.2a3+ (#23, Sep 20 2001, 19:43:51) [MSC 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information.
staticmethod(42) # "object at" output <staticmethod object at 0x00788710> len(staticmethod.__dict__) # and you think this is harmless <wink> 10 staticmethod(42) # whoa! now it's "instance at" output <staticmethod instance at 0x00788710>
That is, just asking for a type's __dict__ can change the values in the type slots. This doesn't seem right -- or does it? gunshyly y'rs - tim
+ Turns out I provoked this by adding classify_class_attrs() to inspect.py, and then foolishly added a test for it <wink>. The symptom can be provoked by one judiciously chosen line:
C:\Code\python\PCbuild>python Python 2.2a3+ (#23, Sep 20 2001, 19:43:51) [MSC 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information.
staticmethod(42) # "object at" output <staticmethod object at 0x00788710> len(staticmethod.__dict__) # and you think this is harmless <wink> 10 staticmethod(42) # whoa! now it's "instance at" output <staticmethod instance at 0x00788710>
That is, just asking for a type's __dict__ can change the values in the type slots. This doesn't seem right -- or does it?
Argh. Sorry. The short answer: my bad, fixed in CVS now. The long answer: There's a default repr() implementation in object.c, which uses "object". This is in PyObject_Repr() when the object doesn't have a tp_repr slot. But there's also a default repr() implementation in typeobject.c, and this one used "instance". (The fix was to change it to use "object" too; it was clearly my intention that this would yield the same output as the default in PyObject_Repr().) The version in typeobject.c is the tp_repr slot of 'object', the universal base class. When a type's initialization is completed by PyType_Ready(), a NULL tp_repr slot is replaced by the tp_repr slot inherited from its base class -- and if there's no explicit base class, the base class 'object' is assumed. Because we currently don't explicitly initialize all type object, most types are auto-initialized on their first attribute requested from one of their instances, by PyObject_GenericGetAttr(): this calls PyType_Ready(tp) if the type is not fully initialized. Asking a type's attribute also auto-initializes the type, because the getattr operation does the same thing that PyObject_GenericGetAttr() does. So repr(T()) does not initialize T, because it doesn't request an attribute (it merely looks at the tp_repr slot). But asking for T.__dict__ *does* initialize T. Yes, this is a mess. We may be better off requesting that all types are initialized explicitly; for most standard types, we can do that in a new function called from Py_Initialize(). But there's a catch: PyType_Ready() uses some other types as helpers, in particular it may create tuples and dictionaries and use them. So at least these two types (and of course strings, which are used as keys) have to be usable without being initialized, in order for their own initialization to be able to proceed. Maybe others. --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (2)
-
Guido van Rossum
-
Tim Peters