Removed the API to create unbound methods and simplified the API for bound methods. The signature is PyMethod_New(func, instance).

Also removed im_class and renamed im_self to __self__ and im_func to __func__. im_class can be substituted with method.__self__.__class__.
I've also updated some parts of the documenation.
This commit is contained in:
Christian Heimes 2007-11-27 10:40:20 +00:00
parent 0d3fb8a944
commit ff737954f3
23 changed files with 152 additions and 268 deletions

View File

@ -49,14 +49,11 @@ attributes:
| | __name__ | name with which this | | | __name__ | name with which this |
| | | method was defined | | | | method was defined |
+-----------+-----------------+---------------------------+ +-----------+-----------------+---------------------------+
| | im_class | class object that asked | | | __func__ | function object |
| | | for this method |
+-----------+-----------------+---------------------------+
| | im_func | function object |
| | | containing implementation | | | | containing implementation |
| | | of method | | | | of method |
+-----------+-----------------+---------------------------+ +-----------+-----------------+---------------------------+
| | im_self | instance to which this | | | __self__ | instance to which this |
| | | method is bound, or | | | | method is bound, or |
| | | ``None`` | | | | ``None`` |
+-----------+-----------------+---------------------------+ +-----------+-----------------+---------------------------+
@ -264,7 +261,7 @@ attributes:
Methods implemented via descriptors that also pass one of the other tests Methods implemented via descriptors that also pass one of the other tests
return false from the :func:`ismethoddescriptor` test, simply because the return false from the :func:`ismethoddescriptor` test, simply because the
other tests promise more -- you can, e.g., count on having the other tests promise more -- you can, e.g., count on having the
:attr:`im_func` attribute (etc) when an object passes :func:`ismethod`. :attr:`__func__` attribute (etc) when an object passes :func:`ismethod`.
.. function:: isdatadescriptor(object) .. function:: isdatadescriptor(object)

View File

@ -17,10 +17,10 @@ non-sensical arguments which crash the interpreter when the object is used.
The :mod:`new` module defines the following functions: The :mod:`new` module defines the following functions:
.. function:: instancemethod(function, instance, class) .. function:: instancemethod(function, instance)
This function will return a method object, bound to *instance*, or unbound if This function will return a method object, bound to *instance*.
*instance* is ``None``. *function* must be callable. *function* must be callable.
.. function:: function(code, globals[, name[, argdefs[, closure]]]) .. function:: function(code, globals[, name[, argdefs[, closure]]])

View File

@ -2216,21 +2216,21 @@ instance methods. Built-in methods are described with the types that support
them. them.
The implementation adds two special read-only attributes to class instance The implementation adds two special read-only attributes to class instance
methods: ``m.im_self`` is the object on which the method operates, and methods: ``m.__self__`` is the object on which the method operates, and
``m.im_func`` is the function implementing the method. Calling ``m(arg-1, ``m.__func__`` is the function implementing the method. Calling ``m(arg-1,
arg-2, ..., arg-n)`` is completely equivalent to calling ``m.im_func(m.im_self, arg-2, ..., arg-n)`` is completely equivalent to calling ``m.__func__(
arg-1, arg-2, ..., arg-n)``. m.__self__, arg-1, arg-2, ..., arg-n)``.
Class instance methods are either *bound* or *unbound*, referring to whether the Class instance methods are either *bound* or *unbound*, referring to whether the
method was accessed through an instance or a class, respectively. When a method method was accessed through an instance or a class, respectively. When a method
is unbound, its ``im_self`` attribute will be ``None`` and if called, an is unbound, its ``__self__`` attribute will be ``None`` and if called, an
explicit ``self`` object must be passed as the first argument. In this case, explicit ``self`` object must be passed as the first argument. In this case,
``self`` must be an instance of the unbound method's class (or a subclass of ``self`` must be an instance of the unbound method's class (or a subclass of
that class), otherwise a :exc:`TypeError` is raised. that class), otherwise a :exc:`TypeError` is raised.
Like function objects, methods objects support getting arbitrary attributes. Like function objects, methods objects support getting arbitrary attributes.
However, since method attributes are actually stored on the underlying function However, since method attributes are actually stored on the underlying function
object (``meth.im_func``), setting method attributes on either bound or unbound object (``meth.__func__``), setting method attributes on either bound or unbound
methods is disallowed. Attempting to set a method attribute results in a methods is disallowed. Attempting to set a method attribute results in a
:exc:`TypeError` being raised. In order to set a method attribute, you need to :exc:`TypeError` being raised. In order to set a method attribute, you need to
explicitly set it on the underlying function object:: explicitly set it on the underlying function object::
@ -2240,7 +2240,7 @@ explicitly set it on the underlying function object::
pass pass
c = C() c = C()
c.method.im_func.whoami = 'my name is c' c.method.__func__.whoami = 'my name is c'
See :ref:`types` for more information. See :ref:`types` for more information.

View File

@ -538,20 +538,18 @@ Callable types
A user-defined method object combines a class, a class instance (or ``None``) A user-defined method object combines a class, a class instance (or ``None``)
and any callable object (normally a user-defined function). and any callable object (normally a user-defined function).
Special read-only attributes: :attr:`im_self` is the class instance object, Special read-only attributes: :attr:`__self__` is the class instance object,
:attr:`im_func` is the function object; :attr:`im_class` is the class of :attr:`__func__` is the function object; :attr:`__doc__` is the method's
:attr:`im_self` for bound methods or the class that asked for the method for documentation (same as ``__func__.__doc__``); :attr:`__name__` is the
unbound methods; :attr:`__doc__` is the method's documentation (same as method name (same as ``__func__.__name__``); :attr:`__module__` is the
``im_func.__doc__``); :attr:`__name__` is the method name (same as name of the module the method was defined in, or ``None`` if unavailable.
``im_func.__name__``); :attr:`__module__` is the name of the module the method
was defined in, or ``None`` if unavailable.
.. index:: .. index::
single: __doc__ (method attribute) single: __doc__ (method attribute)
single: __name__ (method attribute) single: __name__ (method attribute)
single: __module__ (method attribute) single: __module__ (method attribute)
single: im_func (method attribute) single: __func__ (method attribute)
single: im_self (method attribute) single: __self__ (method attribute)
Methods also support accessing (but not setting) the arbitrary function Methods also support accessing (but not setting) the arbitrary function
attributes on the underlying function object. attributes on the underlying function object.
@ -565,49 +563,46 @@ Callable types
the original method object is used as it is. the original method object is used as it is.
.. index:: .. index::
single: im_class (method attribute) single: __func__ (method attribute)
single: im_func (method attribute) single: __self__ (method attribute)
single: im_self (method attribute)
When a user-defined method object is created by retrieving a user-defined When a user-defined method object is created by retrieving a user-defined
function object from a class, its :attr:`im_self` attribute is ``None`` function object from a class, its :attr:`__self__` attribute is ``None``
and the method object is said to be unbound. When one is created by and the method object is said to be unbound. When one is created by
retrieving a user-defined function object from a class via one of its retrieving a user-defined function object from a class via one of its
instances, its :attr:`im_self` attribute is the instance, and the method instances, its :attr:`__self__` attribute is the instance, and the method
object is said to be bound. In either case, the new method's object is said to be bound. Its :attr:`__func__` attribute is the
:attr:`im_class` attribute is the class from which the retrieval takes original function object.
place, and its :attr:`im_func` attribute is the original function object.
.. index:: single: im_func (method attribute) .. index:: single: __func__ (method attribute)
When a user-defined method object is created by retrieving another method object When a user-defined method object is created by retrieving another method object
from a class or instance, the behaviour is the same as for a function object, from a class or instance, the behaviour is the same as for a function object,
except that the :attr:`im_func` attribute of the new instance is not the except that the :attr:`__func__` attribute of the new instance is not the
original method object but its :attr:`im_func` attribute. original method object but its :attr:`__func__` attribute.
.. index:: .. index::
single: im_class (method attribute) single: __func__ (method attribute)
single: im_func (method attribute) single: __self__ (method attribute)
single: im_self (method attribute)
When a user-defined method object is created by retrieving a class method object When a user-defined method object is created by retrieving a class method object
from a class or instance, its :attr:`im_self` attribute is the class itself (the from a class or instance, its :attr:`__self__` attribute is the class itself (the
same as the :attr:`im_class` attribute), and its :attr:`im_func` attribute is same as the :attr:`im_class` attribute), and its :attr:`__func__` attribute is
the function object underlying the class method. the function object underlying the class method.
When an unbound user-defined method object is called, the underlying function When an unbound user-defined method object is called, the underlying function
(:attr:`im_func`) is called, with the restriction that the first argument must (:attr:`__func__`) is called, with the restriction that the first argument must
be an instance of the proper class (:attr:`im_class`) or of a derived class be an instance of the proper class (:attr:`im_class`) or of a derived class
thereof. thereof.
When a bound user-defined method object is called, the underlying function When a bound user-defined method object is called, the underlying function
(:attr:`im_func`) is called, inserting the class instance (:attr:`im_self`) in (:attr:`__func__`) is called, inserting the class instance (:attr:`__self__`) in
front of the argument list. For instance, when :class:`C` is a class which front of the argument list. For instance, when :class:`C` is a class which
contains a definition for a function :meth:`f`, and ``x`` is an instance of contains a definition for a function :meth:`f`, and ``x`` is an instance of
:class:`C`, calling ``x.f(1)`` is equivalent to calling ``C.f(x, 1)``. :class:`C`, calling ``x.f(1)`` is equivalent to calling ``C.f(x, 1)``.
When a user-defined method object is derived from a class method object, the When a user-defined method object is derived from a class method object, the
"class instance" stored in :attr:`im_self` will actually be the class itself, so "class instance" stored in :attr:`__self__` will actually be the class itself, so
that calling either ``x.f(1)`` or ``C.f(1)`` is equivalent to calling ``f(C,1)`` that calling either ``x.f(1)`` or ``C.f(1)`` is equivalent to calling ``f(C,1)``
where ``f`` is the underlying function. where ``f`` is the underlying function.
@ -741,7 +736,7 @@ Custom classes
transformed into an unbound user-defined method object whose :attr:`im_class` transformed into an unbound user-defined method object whose :attr:`im_class`
attribute is :class:`C`. When it would yield a class method object, it is attribute is :class:`C`. When it would yield a class method object, it is
transformed into a bound user-defined method object whose :attr:`im_class` transformed into a bound user-defined method object whose :attr:`im_class`
and :attr:`im_self` attributes are both :class:`C`. When it would yield a and :attr:`__self__` attributes are both :class:`C`. When it would yield a
static method object, it is transformed into the object wrapped by the static static method object, it is transformed into the object wrapped by the static
method object. See section :ref:`descriptors` for another way in which method object. See section :ref:`descriptors` for another way in which
attributes retrieved from a class may differ from those actually contained in attributes retrieved from a class may differ from those actually contained in
@ -786,7 +781,7 @@ Class instances
is the class (call it :class:`C`) of the instance for which the attribute is the class (call it :class:`C`) of the instance for which the attribute
reference was initiated or one of its bases, it is transformed into a bound reference was initiated or one of its bases, it is transformed into a bound
user-defined method object whose :attr:`im_class` attribute is :class:`C` and user-defined method object whose :attr:`im_class` attribute is :class:`C` and
whose :attr:`im_self` attribute is the instance. Static method and class method whose :attr:`__self__` attribute is the instance. Static method and class method
objects are also transformed, as if they had been retrieved from class objects are also transformed, as if they had been retrieved from class
:class:`C`; see above under "Classes". See section :ref:`descriptors` for :class:`C`; see above under "Classes". See section :ref:`descriptors` for
another way in which attributes of a class retrieved via its instances may another way in which attributes of a class retrieved via its instances may

View File

@ -576,8 +576,8 @@ data from a string buffer instead, and pass it as an argument.
.. % \code{sys.stdin} will not cause the interpreter to read further input .. % \code{sys.stdin} will not cause the interpreter to read further input
.. % from it.) .. % from it.)
Instance method objects have attributes, too: ``m.im_self`` is the instance Instance method objects have attributes, too: ``m.__self__`` is the instance
object with the method :meth:`m`, and ``m.im_func`` is the function object object with the method :meth:`m`, and ``m.__func__`` is the function object
corresponding to the method. corresponding to the method.

View File

@ -1,4 +1,4 @@
/* Former class object interface -- now only (un)bound methods are here */ /* Former class object interface -- now only bound methods are here */
/* Revealing some structures (not for general use) */ /* Revealing some structures (not for general use) */
@ -11,8 +11,7 @@ extern "C" {
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject *im_func; /* The callable object implementing the method */ PyObject *im_func; /* The callable object implementing the method */
PyObject *im_self; /* The instance it is bound to, or NULL */ PyObject *im_self; /* The instance it is bound to */
PyObject *im_class; /* The class that asked for the method */
PyObject *im_weakreflist; /* List of weak references */ PyObject *im_weakreflist; /* List of weak references */
} PyMethodObject; } PyMethodObject;
@ -20,7 +19,7 @@ PyAPI_DATA(PyTypeObject) PyMethod_Type;
#define PyMethod_Check(op) ((op)->ob_type == &PyMethod_Type) #define PyMethod_Check(op) ((op)->ob_type == &PyMethod_Type)
PyAPI_FUNC(PyObject *) PyMethod_New(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyMethod_New(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *); PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *);
PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *); PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *);
@ -32,8 +31,6 @@ PyAPI_FUNC(PyObject *) PyMethod_Class(PyObject *);
(((PyMethodObject *)meth) -> im_func) (((PyMethodObject *)meth) -> im_func)
#define PyMethod_GET_SELF(meth) \ #define PyMethod_GET_SELF(meth) \
(((PyMethodObject *)meth) -> im_self) (((PyMethodObject *)meth) -> im_self)
#define PyMethod_GET_CLASS(meth) \
(((PyMethodObject *)meth) -> im_class)
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -14,7 +14,7 @@ class Callbacks(unittest.TestCase):
return args[-1] return args[-1]
def check_type(self, typ, arg): def check_type(self, typ, arg):
PROTO = self.functype.im_func(typ, typ) PROTO = self.functype.__func__(typ, typ)
result = PROTO(self.callback)(arg) result = PROTO(self.callback)(arg)
if typ == c_float: if typ == c_float:
self.failUnlessAlmostEqual(result, arg, places=5) self.failUnlessAlmostEqual(result, arg, places=5)
@ -22,7 +22,7 @@ class Callbacks(unittest.TestCase):
self.failUnlessEqual(self.got_args, (arg,)) self.failUnlessEqual(self.got_args, (arg,))
self.failUnlessEqual(result, arg) self.failUnlessEqual(result, arg)
PROTO = self.functype.im_func(typ, c_byte, typ) PROTO = self.functype.__func__(typ, c_byte, typ)
result = PROTO(self.callback)(-3, arg) result = PROTO(self.callback)(-3, arg)
if typ == c_float: if typ == c_float:
self.failUnlessAlmostEqual(result, arg, places=5) self.failUnlessAlmostEqual(result, arg, places=5)
@ -110,12 +110,12 @@ class Callbacks(unittest.TestCase):
# functions, the type must have a non-NULL stgdict->setfunc. # functions, the type must have a non-NULL stgdict->setfunc.
# POINTER(c_double), for example, is not supported. # POINTER(c_double), for example, is not supported.
prototype = self.functype.im_func(POINTER(c_double)) prototype = self.functype.__func__(POINTER(c_double))
# The type is checked when the prototype is called # The type is checked when the prototype is called
self.assertRaises(TypeError, prototype, lambda: None) self.assertRaises(TypeError, prototype, lambda: None)
def test_unsupported_restype_2(self): def test_unsupported_restype_2(self):
prototype = self.functype.im_func(object) prototype = self.functype.__func__(object)
self.assertRaises(TypeError, prototype, lambda: None) self.assertRaises(TypeError, prototype, lambda: None)
try: try:

View File

@ -18,8 +18,8 @@ def dis(x=None):
if x is None: if x is None:
distb() distb()
return return
if hasattr(x, 'im_func'): if hasattr(x, '__func__'):
x = x.im_func x = x.__func__
if hasattr(x, '__code__'): if hasattr(x, '__code__'):
x = x.__code__ x = x.__code__
if hasattr(x, '__dict__'): if hasattr(x, '__dict__'):

View File

@ -913,7 +913,7 @@ class DocTestFinder:
if isinstance(val, staticmethod): if isinstance(val, staticmethod):
val = getattr(obj, valname) val = getattr(obj, valname)
if isinstance(val, classmethod): if isinstance(val, classmethod):
val = getattr(obj, valname).im_func val = getattr(obj, valname).__func__
# Recurse to methods, properties, and nested classes. # Recurse to methods, properties, and nested classes.
if ((inspect.isfunction(val) or inspect.isclass(val) or if ((inspect.isfunction(val) or inspect.isclass(val) or
@ -985,7 +985,7 @@ class DocTestFinder:
break break
# Find the line number for functions & methods. # Find the line number for functions & methods.
if inspect.ismethod(obj): obj = obj.im_func if inspect.ismethod(obj): obj = obj.__func__
if inspect.isfunction(obj): obj = obj.__code__ if inspect.isfunction(obj): obj = obj.__code__
if inspect.istraceback(obj): obj = obj.tb_frame if inspect.istraceback(obj): obj = obj.tb_frame
if inspect.isframe(obj): obj = obj.f_code if inspect.isframe(obj): obj = obj.f_code

View File

@ -116,7 +116,7 @@ class CallTips:
def _find_constructor(class_ob): def _find_constructor(class_ob):
"Find the nearest __init__() in the class tree." "Find the nearest __init__() in the class tree."
try: try:
return class_ob.__init__.im_func return class_ob.__init__.__func__
except AttributeError: except AttributeError:
for base in class_ob.__bases__: for base in class_ob.__bases__:
init = _find_constructor(base) init = _find_constructor(base)
@ -133,7 +133,7 @@ def get_argspec(ob):
if fob is None: if fob is None:
fob = lambda: None fob = lambda: None
elif isinstance(ob, types.MethodType): elif isinstance(ob, types.MethodType):
fob = ob.im_func fob = ob.__func__
else: else:
fob = ob fob = ob
if isinstance(fob, (types.FunctionType, types.LambdaType)): if isinstance(fob, (types.FunctionType, types.LambdaType)):
@ -183,7 +183,7 @@ def main():
name = t.__name__ name = t.__name__
# exercise fetch_tip(), not just get_argspec() # exercise fetch_tip(), not just get_argspec()
try: try:
qualified_name = "%s.%s" % (t.im_class.__name__, name) qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name)
except AttributeError: except AttributeError:
qualified_name = name qualified_name = name
argspec = ct.fetch_tip(qualified_name) argspec = ct.fetch_tip(qualified_name)

View File

@ -55,9 +55,8 @@ def ismethod(object):
Instance method objects provide these attributes: Instance method objects provide these attributes:
__doc__ documentation string __doc__ documentation string
__name__ name with which this method was defined __name__ name with which this method was defined
im_class class object in which this method belongs __func__ function object containing implementation of method
im_func function object containing implementation of method __self__ instance to which this method is bound"""
im_self instance to which this method is bound"""
return isinstance(object, types.MethodType) return isinstance(object, types.MethodType)
def ismethoddescriptor(object): def ismethoddescriptor(object):
@ -73,7 +72,7 @@ def ismethoddescriptor(object):
Methods implemented via descriptors that also pass one of the other Methods implemented via descriptors that also pass one of the other
tests return false from the ismethoddescriptor() test, simply because tests return false from the ismethoddescriptor() test, simply because
the other tests promise more -- you can, e.g., count on having the the other tests promise more -- you can, e.g., count on having the
im_func attribute (etc) when an object passes ismethod().""" __func__ attribute (etc) when an object passes ismethod()."""
return (hasattr(object, "__get__") return (hasattr(object, "__get__")
and not hasattr(object, "__set__") # else it's a data descriptor and not hasattr(object, "__set__") # else it's a data descriptor
and not ismethod(object) # mutual exclusion and not ismethod(object) # mutual exclusion
@ -351,7 +350,7 @@ def getfile(object):
return object.__file__ return object.__file__
raise TypeError('arg is a built-in class') raise TypeError('arg is a built-in class')
if ismethod(object): if ismethod(object):
object = object.im_func object = object.__func__
if isfunction(object): if isfunction(object):
object = object.__code__ object = object.__code__
if istraceback(object): if istraceback(object):
@ -494,7 +493,7 @@ def findsource(object):
raise IOError('could not find class definition') raise IOError('could not find class definition')
if ismethod(object): if ismethod(object):
object = object.im_func object = object.__func__
if isfunction(object): if isfunction(object):
object = object.__code__ object = object.__code__
if istraceback(object): if istraceback(object):
@ -744,7 +743,7 @@ def getfullargspec(func):
""" """
if ismethod(func): if ismethod(func):
func = func.im_func func = func.__func__
if not isfunction(func): if not isfunction(func):
raise TypeError('arg is not a Python function') raise TypeError('arg is not a Python function')
args, varargs, kwonlyargs, varkw = _getfullargs(func.__code__) args, varargs, kwonlyargs, varkw = _getfullargs(func.__code__)

View File

@ -1081,7 +1081,7 @@ class Misc:
f = CallWrapper(func, subst, self).__call__ f = CallWrapper(func, subst, self).__call__
name = repr(id(f)) name = repr(id(f))
try: try:
func = func.im_func func = func.__func__
except AttributeError: except AttributeError:
pass pass
try: try:

View File

@ -345,8 +345,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
except: except:
func = arg func = arg
try: try:
if hasattr(func, 'im_func'): if hasattr(func, '__func__'):
func = func.im_func func = func.__func__
code = func.__code__ code = func.__code__
#use co_name to identify the bkpt (function names #use co_name to identify the bkpt (function names
#could be aliased, but co_name is invariant) #could be aliased, but co_name is invariant)
@ -789,7 +789,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
print('Function', code.co_name, file=self.stdout) print('Function', code.co_name, file=self.stdout)
return return
# Is it an instance method? # Is it an instance method?
try: code = value.im_func.__code__ try: code = value.__func__.__code__
except: pass except: pass
if code: if code:
print('Method', code.co_name, file=self.stdout) print('Method', code.co_name, file=self.stdout)

View File

@ -848,17 +848,17 @@ class HTMLDoc(Doc):
note = '' note = ''
skipdocs = 0 skipdocs = 0
if inspect.ismethod(object): if inspect.ismethod(object):
imclass = object.im_class imclass = object.__self__.__class__
if cl: if cl:
if imclass is not cl: if imclass is not cl:
note = ' from ' + self.classlink(imclass, mod) note = ' from ' + self.classlink(imclass, mod)
else: else:
if object.im_self is not None: if object.__self__ is not None:
note = ' method of %s instance' % self.classlink( note = ' method of %s instance' % self.classlink(
object.im_self.__class__, mod) object.__self__.__class__, mod)
else: else:
note = ' unbound %s method' % self.classlink(imclass,mod) note = ' unbound %s method' % self.classlink(imclass,mod)
object = object.im_func object = object.__func__
if name == realname: if name == realname:
title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname) title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
@ -1227,17 +1227,17 @@ class TextDoc(Doc):
note = '' note = ''
skipdocs = 0 skipdocs = 0
if inspect.ismethod(object): if inspect.ismethod(object):
imclass = object.im_class imclass = object.__self__.__class__
if cl: if cl:
if imclass is not cl: if imclass is not cl:
note = ' from ' + classname(imclass, mod) note = ' from ' + classname(imclass, mod)
else: else:
if object.im_self is not None: if object.__self__ is not None:
note = ' method of %s instance' % classname( note = ' method of %s instance' % classname(
object.im_self.__class__, mod) object.__self__.__class__, mod)
else: else:
note = ' unbound %s method' % classname(imclass,mod) note = ' unbound %s method' % classname(imclass,mod)
object = object.im_func object = object.__func__
if name == realname: if name == realname:
title = self.bold(realname) title = self.bold(realname)

View File

@ -33,6 +33,6 @@ lst = [None] * 1000000
i = 0 i = 0
del a del a
while 1: while 1:
c.d = 42 # segfaults in PyMethod_New(im_func=D.__set__, im_self=d) c.d = 42 # segfaults in PyMethod_New(__func__=D.__set__, __self__=d)
lst[i] = c.g # consume the free list of instancemethod objects lst[i] = c.g # consume the free list of instancemethod objects
i += 1 i += 1

View File

@ -280,12 +280,12 @@ def test_dir():
c = C() c = C()
vereq(interesting(dir(c)), cstuff) vereq(interesting(dir(c)), cstuff)
#verify('im_self' in dir(C.Cmethod)) #verify('__self__' in dir(C.Cmethod))
c.cdata = 2 c.cdata = 2
c.cmethod = lambda self: 0 c.cmethod = lambda self: 0
vereq(interesting(dir(c)), cstuff + ['cdata', 'cmethod']) vereq(interesting(dir(c)), cstuff + ['cdata', 'cmethod'])
#verify('im_self' in dir(c.Cmethod)) #verify('__self__' in dir(c.Cmethod))
class A(C): class A(C):
Adata = 1 Adata = 1
@ -293,13 +293,13 @@ def test_dir():
astuff = ['Adata', 'Amethod'] + cstuff astuff = ['Adata', 'Amethod'] + cstuff
vereq(interesting(dir(A)), astuff) vereq(interesting(dir(A)), astuff)
#verify('im_self' in dir(A.Amethod)) #verify('__self__' in dir(A.Amethod))
a = A() a = A()
vereq(interesting(dir(a)), astuff) vereq(interesting(dir(a)), astuff)
a.adata = 42 a.adata = 42
a.amethod = lambda self: 3 a.amethod = lambda self: 3
vereq(interesting(dir(a)), astuff + ['adata', 'amethod']) vereq(interesting(dir(a)), astuff + ['adata', 'amethod'])
#verify('im_self' in dir(a.Amethod)) #verify('__self__' in dir(a.Amethod))
# Try a module subclass. # Try a module subclass.
import sys import sys
@ -1418,10 +1418,10 @@ def classmethods():
vereq(ff.__get__(0)(42), (int, 42)) vereq(ff.__get__(0)(42), (int, 42))
# Test super() with classmethods (SF bug 535444) # Test super() with classmethods (SF bug 535444)
veris(C.goo.im_self, C) veris(C.goo.__self__, C)
veris(D.goo.im_self, D) veris(D.goo.__self__, D)
veris(super(D,D).goo.im_self, D) veris(super(D,D).goo.__self__, D)
veris(super(D,d).goo.im_self, D) veris(super(D,d).goo.__self__, D)
vereq(super(D,D).goo(), (D,)) vereq(super(D,D).goo(), (D,))
vereq(super(D,d).goo(), (D,)) vereq(super(D,d).goo(), (D,))
@ -1507,7 +1507,7 @@ def classic():
r = repr(E().foo) r = repr(E().foo)
verify(r.startswith("<bound method E.foo "), r) verify(r.startswith("<bound method E.foo "), r)
r = repr(C.foo.__get__(C())) r = repr(C.foo.__get__(C()))
verify(r.startswith("<bound method ?.foo "), r) verify(r.startswith("<bound method "), r)
def compattr(): def compattr():
if verbose: print("Testing computed attributes...") if verbose: print("Testing computed attributes...")
@ -1687,7 +1687,7 @@ def methods():
vereq(d2.goo(), 1) vereq(d2.goo(), 1)
class E(object): class E(object):
foo = C.foo foo = C.foo
vereq(E().foo.im_func, C.foo) # i.e., unbound vereq(E().foo.__func__, C.foo) # i.e., unbound
r = repr(C.foo.__get__(C(1))) r = repr(C.foo.__get__(C(1)))
verify(r.startswith("<bound method "), r) verify(r.startswith("<bound method "), r)
@ -1864,17 +1864,6 @@ def recursions():
## raise TestFailed, "expected a RuntimeError for print recursion" ## raise TestFailed, "expected a RuntimeError for print recursion"
## sys.stdout = test_stdout ## sys.stdout = test_stdout
# Bug #1202533.
class A(object):
pass
A.__mul__ = new.instancemethod(lambda self, x: self * x, None, A)
try:
A()*2
except RuntimeError:
pass
else:
raise TestFailed("expected a RuntimeError")
def weakrefs(): def weakrefs():
if verbose: print("Testing weak references...") if verbose: print("Testing weak references...")
import weakref import weakref

View File

@ -104,11 +104,12 @@ else: raise TestFailed
if f2.a.one != f1.a.one != F.a.one != 11: if f2.a.one != f1.a.one != F.a.one != 11:
raise TestFailed raise TestFailed
# im_func may not be a Python method! # __func__ may not be a Python method!
import new import new
F.id = new.instancemethod(id, None, F) F.id = id
eff = F() eff = F()
eff.id = new.instancemethod(id, eff)
if eff.id() != id(eff): if eff.id() != id(eff):
raise TestFailed raise TestFailed
@ -296,32 +297,32 @@ def test_func_dict():
verify(f.__dict__ == {'world': 'hello'}) verify(f.__dict__ == {'world': 'hello'})
cantset(f, "__dict__", None) cantset(f, "__dict__", None)
def test_im_class(): def test___self__():
class C: class C:
def foo(self): pass def foo(self): pass
#verify(C.foo.im_class is C) #verify(C.foo.__self__.__class__ is C)
verify(C().foo.im_class is C) verify(C().foo.__self__.__class__ is C)
#cantset(C.foo, "im_class", C) #cantset(C.foo, "__self__.__class__", C)
cantset(C().foo, "im_class", C) cantset(C().foo, "__self__.__class__", C)
def test_im_func(): def test___func__():
def foo(self): pass def foo(self): pass
class C: class C:
pass pass
C.foo = foo C.foo = foo
#verify(C.foo.im_func is foo) #verify(C.foo.__func__ is foo)
verify(C().foo.im_func is foo) verify(C().foo.__func__ is foo)
#cantset(C.foo, "im_func", foo) #cantset(C.foo, "__func__", foo)
cantset(C().foo, "im_func", foo) cantset(C().foo, "__func__", foo)
def test_im_self(): def test___self__():
class C: class C:
def foo(self): pass def foo(self): pass
#verify(C.foo.im_self is None) #verify(C.foo.__self__ is None)
c = C() c = C()
#verify(c.foo.im_self is c) #verify(c.foo.__self__ is c)
#cantset(C.foo, "im_self", None) #cantset(C.foo, "__self__", None)
#cantset(c.foo, "im_self", c) #cantset(c.foo, "__self__", c)
def test_im_dict(): def test_im_dict():
class C: class C:
@ -358,9 +359,9 @@ def testmore():
test_func_defaults() test_func_defaults()
test_func_dict() test_func_dict()
# Tests for instance method attributes # Tests for instance method attributes
test_im_class() test___self__()
test_im_func() test___func__()
test_im_self() test___self__()
test_im_dict() test_im_dict()
test_im_doc() test_im_doc()
test_im_name() test_im_name()

View File

@ -25,7 +25,7 @@ class NewTest(unittest.TestCase):
# new.instancemethod() # new.instancemethod()
c = C() c = C()
c.yolks = 3 c.yolks = 3
im = new.instancemethod(break_yolks, c, C) im = new.instancemethod(break_yolks, c)
self.assertEqual(c.get_yolks(), 3, self.assertEqual(c.get_yolks(), 3,
'Broken call of hand-crafted class instance') 'Broken call of hand-crafted class instance')
@ -43,7 +43,7 @@ class NewTest(unittest.TestCase):
self.assertEqual(c.get_yolks(), -1) self.assertEqual(c.get_yolks(), -1)
# Verify that dangerous instance method creation is forbidden # Verify that dangerous instance method creation is forbidden
self.assertRaises(TypeError, new.instancemethod, break_yolks, None) self.assertRaises(TypeError, new.instancemethod, None)
# Verify that instancemethod() doesn't allow keyword args # Verify that instancemethod() doesn't allow keyword args
self.assertRaises(TypeError, new.instancemethod, break_yolks, c, kw=1) self.assertRaises(TypeError, new.instancemethod, break_yolks, c, kw=1)

View File

@ -31,7 +31,7 @@ class HookWatcher:
def get_events(self): def get_events(self):
"""Remove calls to add_event().""" """Remove calls to add_event()."""
disallowed = [ident(self.add_event.im_func), ident(ident)] disallowed = [ident(self.add_event.__func__), ident(ident)]
self.frames = None self.frames = None
return [item for item in self.events if item[2] not in disallowed] return [item for item in self.events if item[2] not in disallowed]

View File

@ -67,7 +67,7 @@ class PyclbrTest(TestCase):
if isinstance(obj, MethodType): if isinstance(obj, MethodType):
# could be a classmethod # could be a classmethod
if (not isinstance(classdict[name], ClassMethodType) or if (not isinstance(classdict[name], ClassMethodType) or
obj.im_self is not oclass): obj.__self__ is not oclass):
return False return False
elif not isinstance(obj, FunctionType): elif not isinstance(obj, FunctionType):
return False return False

View File

@ -44,6 +44,12 @@ Core and Builtins
- Renamed structmember.h WRITE_RESTRICTED to PY_WRITE_RESTRICTED to work - Renamed structmember.h WRITE_RESTRICTED to PY_WRITE_RESTRICTED to work
around a name clash with VS 2008 on Windows. around a name clash with VS 2008 on Windows.
- Unbound methods are gone for good. ClassObject.method returns an ordinary
function object, instance.method still returns a bound method object.
The API of bound methods is cleaned up, too. The im_class attribute is
removed and im_func + im_self are renamed to __func__ and __self__. The
factory PyMethod_New takes only func and instance as argument.
Extension Modules Extension Modules
----------------- -----------------

View File

@ -26,33 +26,25 @@ PyMethod_Self(PyObject *im)
return ((PyMethodObject *)im)->im_self; return ((PyMethodObject *)im)->im_self;
} }
PyObject * /* Method objects are used for bound instance methods returned by
PyMethod_Class(PyObject *im) instancename.methodname. ClassName.methodname returns an ordinary
{ function.
if (!PyMethod_Check(im)) {
PyErr_BadInternalCall();
return NULL;
}
return ((PyMethodObject *)im)->im_class;
}
/* Method objects are used for two purposes:
(a) as bound instance methods (returned by instancename.methodname)
(b) as unbound methods (returned by ClassName.methodname)
In case (b), im_self is NULL
*/ */
static PyMethodObject *free_list; static PyMethodObject *free_list;
PyObject * PyObject *
PyMethod_New(PyObject *func, PyObject *self, PyObject *klass) PyMethod_New(PyObject *func, PyObject *self)
{ {
register PyMethodObject *im; register PyMethodObject *im;
if (!PyCallable_Check(func)) { if (!PyCallable_Check(func)) {
PyErr_BadInternalCall(); PyErr_BadInternalCall();
return NULL; return NULL;
} }
if (self == NULL) {
PyErr_BadInternalCall();
return NULL;
}
im = free_list; im = free_list;
if (im != NULL) { if (im != NULL) {
free_list = (PyMethodObject *)(im->im_self); free_list = (PyMethodObject *)(im->im_self);
@ -68,25 +60,21 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *klass)
im->im_func = func; im->im_func = func;
Py_XINCREF(self); Py_XINCREF(self);
im->im_self = self; im->im_self = self;
Py_XINCREF(klass);
im->im_class = klass;
_PyObject_GC_TRACK(im); _PyObject_GC_TRACK(im);
return (PyObject *)im; return (PyObject *)im;
} }
/* Descriptors for PyMethod attributes */ /* Descriptors for PyMethod attributes */
/* im_class, im_func and im_self are stored in the PyMethod object */ /* im_func and im_self are stored in the PyMethod object */
#define OFF(x) offsetof(PyMethodObject, x) #define OFF(x) offsetof(PyMethodObject, x)
static PyMemberDef method_memberlist[] = { static PyMemberDef method_memberlist[] = {
{"im_class", T_OBJECT, OFF(im_class), READONLY|RESTRICTED, {"__func__", T_OBJECT, OFF(im_func), READONLY|RESTRICTED,
"the class associated with a method"},
{"im_func", T_OBJECT, OFF(im_func), READONLY|RESTRICTED,
"the function (or other callable) implementing a method"}, "the function (or other callable) implementing a method"},
{"im_self", T_OBJECT, OFF(im_self), READONLY|RESTRICTED, {"__self__", T_OBJECT, OFF(im_self), READONLY|RESTRICTED,
"the instance to which a method is bound; None for unbound methods"}, "the instance to which a method is bound"},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
@ -141,7 +129,7 @@ method_getattro(PyObject *obj, PyObject *name)
} }
PyDoc_STRVAR(method_doc, PyDoc_STRVAR(method_doc,
"method(function, instance, class)\n\ "method(function, instance)\n\
\n\ \n\
Create an instance method object."); Create an instance method object.");
@ -150,27 +138,24 @@ method_new(PyTypeObject* type, PyObject* args, PyObject *kw)
{ {
PyObject *func; PyObject *func;
PyObject *self; PyObject *self;
PyObject *classObj = NULL;
if (!_PyArg_NoKeywords("instancemethod", kw)) if (!_PyArg_NoKeywords("instancemethod", kw))
return NULL; return NULL;
if (!PyArg_UnpackTuple(args, "method", 2, 3, if (!PyArg_UnpackTuple(args, "method", 2, 3,
&func, &self, &classObj)) &func, &self))
return NULL; return NULL;
if (!PyCallable_Check(func)) { if (!PyCallable_Check(func)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"first argument must be callable"); "first argument must be callable");
return NULL; return NULL;
} }
if (self == Py_None) if (self == NULL || self == Py_None) {
self = NULL;
if (self == NULL && classObj == NULL) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"unbound methods must have non-NULL im_class"); "self must not be None");
return NULL; return NULL;
} }
return PyMethod_New(func, self, classObj); return PyMethod_New(func, self);
} }
static void static void
@ -181,7 +166,6 @@ method_dealloc(register PyMethodObject *im)
PyObject_ClearWeakRefs((PyObject *)im); PyObject_ClearWeakRefs((PyObject *)im);
Py_DECREF(im->im_func); Py_DECREF(im->im_func);
Py_XDECREF(im->im_self); Py_XDECREF(im->im_self);
Py_XDECREF(im->im_class);
im->im_self = (PyObject *)free_list; im->im_self = (PyObject *)free_list;
free_list = im; free_list = im;
} }
@ -225,10 +209,15 @@ method_repr(PyMethodObject *a)
{ {
PyObject *self = a->im_self; PyObject *self = a->im_self;
PyObject *func = a->im_func; PyObject *func = a->im_func;
PyObject *klass = a->im_class; PyObject *klass = (PyObject*)Py_Type(self);
PyObject *funcname = NULL ,*klassname = NULL, *result = NULL; PyObject *funcname = NULL ,*klassname = NULL, *result = NULL;
char *defname = "?"; char *defname = "?";
if (self == NULL) {
PyErr_BadInternalCall();
return NULL;
}
funcname = PyObject_GetAttrString(func, "__name__"); funcname = PyObject_GetAttrString(func, "__name__");
if (funcname == NULL) { if (funcname == NULL) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) if (!PyErr_ExceptionMatches(PyExc_AttributeError))
@ -239,6 +228,7 @@ method_repr(PyMethodObject *a)
Py_DECREF(funcname); Py_DECREF(funcname);
funcname = NULL; funcname = NULL;
} }
if (klass == NULL) if (klass == NULL)
klassname = NULL; klassname = NULL;
else { else {
@ -253,16 +243,12 @@ method_repr(PyMethodObject *a)
klassname = NULL; klassname = NULL;
} }
} }
if (self == NULL)
result = PyUnicode_FromFormat("<unbound method %V.%V>",
klassname, defname,
funcname, defname);
else {
/* XXX Shouldn't use repr()/%R here! */ /* XXX Shouldn't use repr()/%R here! */
result = PyUnicode_FromFormat("<bound method %V.%V of %R>", result = PyUnicode_FromFormat("<bound method %V.%V of %R>",
klassname, defname, klassname, defname,
funcname, defname, self); funcname, defname, self);
}
Py_XDECREF(funcname); Py_XDECREF(funcname);
Py_XDECREF(klassname); Py_XDECREF(klassname);
return result; return result;
@ -292,93 +278,20 @@ method_traverse(PyMethodObject *im, visitproc visit, void *arg)
{ {
Py_VISIT(im->im_func); Py_VISIT(im->im_func);
Py_VISIT(im->im_self); Py_VISIT(im->im_self);
Py_VISIT(im->im_class);
return 0; return 0;
} }
static void
getclassname(PyObject *klass, char *buf, int bufsize)
{
PyObject *name;
assert(bufsize > 1);
strcpy(buf, "?"); /* Default outcome */
if (klass == NULL)
return;
name = PyObject_GetAttrString(klass, "__name__");
if (name == NULL) {
/* This function cannot return an exception */
PyErr_Clear();
return;
}
if (PyUnicode_Check(name)) {
strncpy(buf, PyUnicode_AsString(name), bufsize);
buf[bufsize-1] = '\0';
}
Py_DECREF(name);
}
static void
getinstclassname(PyObject *inst, char *buf, int bufsize)
{
PyObject *klass;
if (inst == NULL) {
assert(bufsize > 0 && (size_t)bufsize > strlen("nothing"));
strcpy(buf, "nothing");
return;
}
klass = PyObject_GetAttrString(inst, "__class__");
if (klass == NULL) {
/* This function cannot return an exception */
PyErr_Clear();
klass = (PyObject *)(inst->ob_type);
Py_INCREF(klass);
}
getclassname(klass, buf, bufsize);
Py_XDECREF(klass);
}
static PyObject * static PyObject *
method_call(PyObject *func, PyObject *arg, PyObject *kw) method_call(PyObject *func, PyObject *arg, PyObject *kw)
{ {
PyObject *self = PyMethod_GET_SELF(func); PyObject *self = PyMethod_GET_SELF(func);
PyObject *klass = PyMethod_GET_CLASS(func);
PyObject *result; PyObject *result;
func = PyMethod_GET_FUNCTION(func); func = PyMethod_GET_FUNCTION(func);
if (self == NULL) { if (self == NULL) {
/* Unbound methods must be called with an instance of PyErr_BadInternalCall();
the class (or a derived class) as first argument */
int ok;
if (PyTuple_Size(arg) >= 1)
self = PyTuple_GET_ITEM(arg, 0);
if (self == NULL)
ok = 0;
else {
ok = PyObject_IsInstance(self, klass);
if (ok < 0)
return NULL; return NULL;
} }
if (!ok) {
char clsbuf[256];
char instbuf[256];
getclassname(klass, clsbuf, sizeof(clsbuf));
getinstclassname(self, instbuf, sizeof(instbuf));
PyErr_Format(PyExc_TypeError,
"unbound method %s%s must be called with "
"%s instance as first argument "
"(got %s%s instead)",
PyEval_GetFuncName(func),
PyEval_GetFuncDesc(func),
clsbuf,
instbuf,
self == NULL ? "" : " instance");
return NULL;
}
Py_INCREF(arg);
}
else { else {
Py_ssize_t argcount = PyTuple_Size(arg); Py_ssize_t argcount = PyTuple_Size(arg);
PyObject *newarg = PyTuple_New(argcount + 1); PyObject *newarg = PyTuple_New(argcount + 1);
@ -402,27 +315,15 @@ method_call(PyObject *func, PyObject *arg, PyObject *kw)
static PyObject * static PyObject *
method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls) method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls)
{ {
/* Don't rebind an already bound method, or an unbound method /* Don't rebind an already bound method of a class that's not a base
of a class that's not a base class of cls. */ class of cls. */
if (PyMethod_GET_SELF(meth) != NULL) { if (PyMethod_GET_SELF(meth) != NULL) {
/* Already bound */ /* Already bound */
Py_INCREF(meth); Py_INCREF(meth);
return meth; return meth;
} }
/* No, it is an unbound method */
if (PyMethod_GET_CLASS(meth) != NULL && cls != NULL) {
/* Do subclass test. If it fails, return meth unchanged. */
int ok = PyObject_IsSubclass(cls, PyMethod_GET_CLASS(meth));
if (ok < 0)
return NULL;
if (!ok) {
Py_INCREF(meth);
return meth;
}
}
/* Bind it to obj */ /* Bind it to obj */
return PyMethod_New(PyMethod_GET_FUNCTION(meth), obj, cls); return PyMethod_New(PyMethod_GET_FUNCTION(meth), obj);
} }
PyTypeObject PyMethod_Type = { PyTypeObject PyMethod_Type = {

View File

@ -647,7 +647,7 @@ func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
Py_INCREF(func); Py_INCREF(func);
return func; return func;
} }
return PyMethod_New(func, obj, type); return PyMethod_New(func, obj);
} }
PyTypeObject PyFunction_Type = { PyTypeObject PyFunction_Type = {
@ -751,8 +751,7 @@ cm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
} }
if (type == NULL) if (type == NULL)
type = (PyObject *)(Py_Type(obj)); type = (PyObject *)(Py_Type(obj));
return PyMethod_New(cm->cm_callable, return PyMethod_New(cm->cm_callable, type);
type, (PyObject *)(Py_Type(type)));
} }
static int static int