[Python-ideas] Class introspection by pydoc vs. attributes on meta classes

Nick Coghlan ncoghlan at gmail.com
Sun Jul 26 16:09:09 CEST 2015


On 25 July 2015 at 21:47, Ronald Oussoren <ronaldoussoren at mac.com> wrote:
> The issue was found in the context of PyObjC (obviously…):  Objective-C
> classes can and do have instance and class methods of the same name. Those
> cannot both be a descriptor in the Python proxy class, and that’s why PyObjC
> uses an metaclass to expose Objective-C class methods to Python code. This
> works very well, except for some problems with introspection such as the
> pydoc issue I refer to above.

The main problem with doing that by default is the number of methods
and attributes that exist on type:

>>> dir(type)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__',
'__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__',
'__dir__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__
', '__getattribute__', '__gt__', '__hash__', '__init__',
'__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__',
'__mro__', '__name__', '__ne__', '__new__', '__prepare__',
'__qualname__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasscheck__', '__subclasses__',
'__subclasshook__', '__text_signature__', '__weakrefoffset__', 'mro']

We *don't* want all of those turning up in the API reported for every
class object, so we don't go to the metaclass by default in dir(), and
hence not in help() output either.

However, if you override __dir__ on the metaclass, you also affect
pydoc output on instances of that metaclass:

>>> class M(type):
...     def __dir__(cls):
...         return ["meta_method"]
...     def meta_method(cls):
...         """Metaclass method"""
...         return 42
...
>>> class C(metaclass=M): pass
...
>>> C.meta_method()
42
>>> dir(C)
['meta_method']
>>> print(pydoc.render_doc(C))
Python Library Documentation: class C in module __main__

class C(builtins.object)
|  Methods inherited from M:
|
|  meta_method() from __main__.M
|      Metaclass method

It actually slightly confuses pydoc (it's assuming any method not
defined locally is "inherited", which isn't really the right word for
metaclass methods), but it's good enough to get people going in the
right direction.

And dynamically reverting C back to the default help output to
demonstrate that it really is the __dir__ that makes the difference:

>>> del M.__dir__
>>> print(pydoc.render_doc(C))
Python Library Documentation: class C in module __main__

class C(builtins.object)
|  Data descriptors defined here:
|
|  __dict__
|      dictionary for instance variables (if defined)
|
|  __weakref__
|      list of weak references to the object (if defined)

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list