Reputation: 41
I have recently tried to implement a "pass through" on my a few classes to allow calling methods on specific properties of a classes using the __getattr__
method. This has broken (sort-of) autocompleting in ipython and I am curious to understand why.
First:
class MyClass(object):
def __init__():
#create an instance of class ClassProperty
self.class_property = ClassProperty()
def __getattr__(self, item):
#pass through calls to non-existent methods, print first
print item
return getattr(self.class_property,item)
When creating an instance of this class and then attempting to tab-complete in Ipython (0.12.1), it seems that several functions are called including __methods__
, trait_names
and _getAttributeNames
.
In [1]: my_class = MyClass()
In [2]: my_class.not_in_my_class<tab>
__methods__
trait_names
_getAttributeNames
I am curious to understand what is happening.
Thanks,
P.S. I know that doing this is very opaque and is temporary. Still, I'm curious.
UPDATE
Based on what I learned from the accepted answer below I have been able to successfully "pass through" autocompletion so that methods that exist in ClassProperty
now autocomplete on instances of MyClass
. What was required to successfully pass through was an update to the __dir__
method
class MyClass(object):
def __init__():
#create an instance of class ClassProperty
self.class_property = ClassProperty()
def __getattr__(self, item):
#pass through calls to non-existent methods, print first
print item
return getattr(self.class_property,item)
def __dir__(self):
dir_list = self.__dict__.keys()
try:
dir_list.extend(dir(self.class_property))
except:
pass
return dir_list
The reason why I have implemented this is because we are using python as an instrument control package with an ipython command line interface. We have devices (i.e. a laser) that selects an actuator based on json config file. We want to have top level access to the methods on the actuator for user convenience.
I would be curious if there are thoughts on my approach and if I am inviting any problems down the road.
Upvotes: 3
Views: 820
Reputation: 281624
IPython is asking your object for a list of its attributes, trying several protocols of varying levels of obscurity. None of these protocols are part of the core Python language (at least, not any more), and the current IPython source code seems to have removed most of these checks.
__methods__
is a really old part of Python that was phased out when they unified the type and class systems way back in 2.2, about 14 years ago.
It used to be the case that instances of built-in types would provide a __methods__
attribute that would be a list of all the methods they supported. This required an instance of the type, and the system it was part of had a number of other flaws.
In contrast, user-defined classes provided the introspection system you're familiar with from modern Python, with object and class __dict__
s. This was more powerful and more consistent than the other system, so when they unified types and classes, the class-based introspection system was kept and the other was removed.
__methods__
is pretty much completely gone now. There might be some third-party legacy code still providing a __methods__
attribute, but the only thing that comes with Python that still supports __methods__
seems to be this weird thing in idlelib. In Python 2, dir
still tries to look for it, though, and IPython's tab completion uses dir
, so that's where the __methods__
access is coming from. There should also be an attempt to access __members__
, which you might not have noticed due to the message appearing on the same line you were typing on.
_getAttributeNames
is a protocol apparently introduced by PyCrust, an alternative Python shell. If defined, a _getAttributeNames
method would provide a list of all dynamic attributes an object supports through __getattr__
. (This was before __getattribute__
existed.)
_getAttributeNames
appears to have been introduced around 2001, making it newer than __methods__
, which disappeared around the same time frame. Some time since IPython 0.12.1, the _getAttributeNames
check has been removed from IPython.
trait_names
comes from Enthought Traits. For a class that uses traits, the trait_names
method is an instance method that returns a list of the names of its traits. It also accepts search criteria to list specific traits. Like _getAttributeNames
, this has been removed from IPython, apparently because the check isn't needed to handle traits (any more?).
Upvotes: 5