[Python-Dev] A baffler in test_repr

Guido van Rossum guido@python.org
Mon, 24 Sep 2001 12:12:25 -0400


> + 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/)