Search code examples
pythonpython-3.xpython-3.5python-c-api

Where can I find an instancemethod in the Python 3 standard library?


I'm trying to test for and fix a bug in pprint++ (edit: the correct link; original link left for posterity) which is coming up because the instancemethod type is not hashable:

In [16]: import pandas as pd

In [17]: type(pd.tslib.NaT).__repr__
Out[17]: <instancemethod __repr__ at 0x1058d2be8>

In [18]: hash(type(pd.tslib.NaT).__repr__)
   ...
TypeError: unhashable type: 'instancemethod'

But I'm having trouble testing for this issue because I don't know where else I can find an instancemethod in the Python 3 standard library, and I don't want my tests to depend on Pandas.

Specifically, it seems like the "normal" builtin types have "instance methods" that are implemented slightly differently:

In [19]: type(None).__repr__
Out[19]: <slot wrapper '__repr__' of 'NoneType' objects>

In [20]: hash(type(None).__repr__)
Out[20]: -9223372036583849574

So: where can I find an instancemethod in the Python 3 standard library so I can write tests against it? Or is it a special type that doesn't appear there?

(note: this only appears to affect Python 3, as the same method in Python 2 is an unbound method, which is hashable)


Solution

  • This type isn't used in anything that comes with Python, and there's no Python-level API to create objects of this type. However, you can do it with a direct C API call:

    import ctypes
    
    PyInstanceMethod_New = ctypes.pythonapi.PyInstanceMethod_New
    PyInstanceMethod_New.argtypes = (ctypes.py_object,)
    PyInstanceMethod_New.restype = ctypes.py_object
    
    arbitrary_callable = sum
    
    instance_method = PyInstanceMethod_New(arbitrary_callable)
    

    The name instancemethod looks a lot like a bound method object, but it turns out it's something else entirely. It's a weird internal thing that, according to its documentation, is supposed to be the new way for C types to represent their methods, except that the standard C-level API for creating a type doesn't actually use it.

    According to conversations on the Python issue tracker, this feature was requested by the developers of Cython and Pyrex. It looks like pandas.tslib.NaT is implemented in Cython, and the Cython implementation actually uses this type, where the standard C API for creating types doesn't.

    Note that the situation is completely different on Python 2. On Python 2, this new type didn't exist, and instancemethod was the name of the type of method objects representing ordinary methods written in Python. In Python 3, the new type took that name, and the type of method objects for methods written in Python is now named method.