Commit ff737954 authored by Christian Heimes's avatar Christian Heimes

Removed the API to create unbound methods and simplified the API for bound...

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.
parent 0d3fb8a9
......@@ -49,14 +49,11 @@ attributes:
| | __name__ | name with which this |
| | | method was defined |
+-----------+-----------------+---------------------------+
| | im_class | class object that asked |
| | | for this method |
+-----------+-----------------+---------------------------+
| | im_func | function object |
| | __func__ | function object |
| | | containing implementation |
| | | of method |
+-----------+-----------------+---------------------------+
| | im_self | instance to which this |
| | __self__ | instance to which this |
| | | method is bound, or |
| | | ``None`` |
+-----------+-----------------+---------------------------+
......@@ -264,7 +261,7 @@ attributes:
Methods implemented via descriptors that also pass one of the other tests
return false from the :func:`ismethoddescriptor` test, simply because 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)
......
......@@ -17,10 +17,10 @@ non-sensical arguments which crash the interpreter when the object is used.
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
*instance* is ``None``. *function* must be callable.
This function will return a method object, bound to *instance*.
*function* must be callable.
.. function:: function(code, globals[, name[, argdefs[, closure]]])
......
......@@ -2216,21 +2216,21 @@ instance methods. Built-in methods are described with the types that support
them.
The implementation adds two special read-only attributes to class instance
methods: ``m.im_self`` is the object on which the method operates, and
``m.im_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-1, arg-2, ..., arg-n)``.
methods: ``m.__self__`` is the object on which the method operates, and
``m.__func__`` is the function implementing the method. Calling ``m(arg-1,
arg-2, ..., arg-n)`` is completely equivalent to calling ``m.__func__(
m.__self__, arg-1, arg-2, ..., arg-n)``.
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
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,
``self`` must be an instance of the unbound method's class (or a subclass of
that class), otherwise a :exc:`TypeError` is raised.
Like function objects, methods objects support getting arbitrary attributes.
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
:exc:`TypeError` being raised. In order to set a method attribute, you need to
explicitly set it on the underlying function object::
......@@ -2240,7 +2240,7 @@ explicitly set it on the underlying function object::
pass
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.
......
......@@ -538,20 +538,18 @@ Callable types
A user-defined method object combines a class, a class instance (or ``None``)
and any callable object (normally a user-defined function).
Special read-only attributes: :attr:`im_self` is the class instance object,
:attr:`im_func` is the function object; :attr:`im_class` is the class of
:attr:`im_self` for bound methods or the class that asked for the method for
unbound methods; :attr:`__doc__` is the method's documentation (same as
``im_func.__doc__``); :attr:`__name__` is the method name (same as
``im_func.__name__``); :attr:`__module__` is the name of the module the method
was defined in, or ``None`` if unavailable.
Special read-only attributes: :attr:`__self__` is the class instance object,
:attr:`__func__` is the function object; :attr:`__doc__` is the method's
documentation (same as ``__func__.__doc__``); :attr:`__name__` is the
method name (same as ``__func__.__name__``); :attr:`__module__` is the
name of the module the method was defined in, or ``None`` if unavailable.
.. index::
single: __doc__ (method attribute)
single: __name__ (method attribute)
single: __module__ (method attribute)
single: im_func (method attribute)
single: im_self (method attribute)
single: __func__ (method attribute)
single: __self__ (method attribute)
Methods also support accessing (but not setting) the arbitrary function
attributes on the underlying function object.
......@@ -565,49 +563,46 @@ Callable types
the original method object is used as it is.
.. index::
single: im_class (method attribute)
single: im_func (method attribute)
single: im_self (method attribute)
single: __func__ (method attribute)
single: __self__ (method attribute)
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
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
object is said to be bound. In either case, the new method's
:attr:`im_class` attribute is the class from which the retrieval takes
place, and its :attr:`im_func` attribute is the original function object.
instances, its :attr:`__self__` attribute is the instance, and the method
object is said to be bound. Its :attr:`__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
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
original method object but its :attr:`im_func` attribute.
except that the :attr:`__func__` attribute of the new instance is not the
original method object but its :attr:`__func__` attribute.
.. index::
single: im_class (method attribute)
single: im_func (method attribute)
single: im_self (method attribute)
single: __func__ (method attribute)
single: __self__ (method attribute)
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
same as the :attr:`im_class` attribute), and its :attr:`im_func` attribute is
from a class or instance, its :attr:`__self__` attribute is the class itself (the
same as the :attr:`im_class` attribute), and its :attr:`__func__` attribute is
the function object underlying the class method.
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
thereof.
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
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)``.
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)``
where ``f`` is the underlying function.
......@@ -741,7 +736,7 @@ Custom classes
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
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
method object. See section :ref:`descriptors` for another way in which
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
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
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
:class:`C`; see above under "Classes". See section :ref:`descriptors` for
another way in which attributes of a class retrieved via its instances may
......
......@@ -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
.. % from it.)
Instance method objects have attributes, too: ``m.im_self`` is the instance
object with the method :meth:`m`, and ``m.im_func`` is the function object
Instance method objects have attributes, too: ``m.__self__`` is the instance
object with the method :meth:`m`, and ``m.__func__`` is the function object
corresponding to the method.
......
/* 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) */
......@@ -11,8 +11,7 @@ extern "C" {
typedef struct {
PyObject_HEAD
PyObject *im_func; /* The callable object implementing the method */
PyObject *im_self; /* The instance it is bound to, or NULL */
PyObject *im_class; /* The class that asked for the method */
PyObject *im_self; /* The instance it is bound to */
PyObject *im_weakreflist; /* List of weak references */
} PyMethodObject;
......@@ -20,7 +19,7 @@ PyAPI_DATA(PyTypeObject) 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_Self(PyObject *);
......@@ -32,8 +31,6 @@ PyAPI_FUNC(PyObject *) PyMethod_Class(PyObject *);
(((PyMethodObject *)meth) -> im_func)
#define PyMethod_GET_SELF(meth) \
(((PyMethodObject *)meth) -> im_self)
#define PyMethod_GET_CLASS(meth) \
(((PyMethodObject *)meth) -> im_class)
#ifdef __cplusplus
}
......
......@@ -14,7 +14,7 @@ class Callbacks(unittest.TestCase):
return args[-1]
def check_type(self, typ, arg):
PROTO = self.functype.im_func(typ, typ)
PROTO = self.functype.__func__(typ, typ)
result = PROTO(self.callback)(arg)
if typ == c_float:
self.failUnlessAlmostEqual(result, arg, places=5)
......@@ -22,7 +22,7 @@ class Callbacks(unittest.TestCase):
self.failUnlessEqual(self.got_args, (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)
if typ == c_float:
self.failUnlessAlmostEqual(result, arg, places=5)
......@@ -110,12 +110,12 @@ class Callbacks(unittest.TestCase):
# functions, the type must have a non-NULL stgdict->setfunc.
# 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
self.assertRaises(TypeError, prototype, lambda: None)
def test_unsupported_restype_2(self):
prototype = self.functype.im_func(object)
prototype = self.functype.__func__(object)
self.assertRaises(TypeError, prototype, lambda: None)
try:
......
......@@ -18,8 +18,8 @@ def dis(x=None):
if x is None:
distb()
return
if hasattr(x, 'im_func'):
x = x.im_func
if hasattr(x, '__func__'):
x = x.__func__
if hasattr(x, '__code__'):
x = x.__code__
if hasattr(x, '__dict__'):
......
......@@ -913,7 +913,7 @@ class DocTestFinder:
if isinstance(val, staticmethod):
val = getattr(obj, valname)
if isinstance(val, classmethod):
val = getattr(obj, valname).im_func
val = getattr(obj, valname).__func__
# Recurse to methods, properties, and nested classes.
if ((inspect.isfunction(val) or inspect.isclass(val) or
......@@ -985,7 +985,7 @@ class DocTestFinder:
break
# 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.istraceback(obj): obj = obj.tb_frame
if inspect.isframe(obj): obj = obj.f_code
......
......@@ -116,7 +116,7 @@ class CallTips:
def _find_constructor(class_ob):
"Find the nearest __init__() in the class tree."
try:
return class_ob.__init__.im_func
return class_ob.__init__.__func__
except AttributeError:
for base in class_ob.__bases__:
init = _find_constructor(base)
......@@ -133,7 +133,7 @@ def get_argspec(ob):
if fob is None:
fob = lambda: None
elif isinstance(ob, types.MethodType):
fob = ob.im_func
fob = ob.__func__
else:
fob = ob
if isinstance(fob, (types.FunctionType, types.LambdaType)):
......@@ -183,7 +183,7 @@ def main():
name = t.__name__
# exercise fetch_tip(), not just get_argspec()
try:
qualified_name = "%s.%s" % (t.im_class.__name__, name)
qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name)
except AttributeError:
qualified_name = name
argspec = ct.fetch_tip(qualified_name)
......
......@@ -55,9 +55,8 @@ def ismethod(object):
Instance method objects provide these attributes:
__doc__ documentation string
__name__ name with which this method was defined
im_class class object in which this method belongs
im_func function object containing implementation of method
im_self instance to which this method is bound"""
__func__ function object containing implementation of method
__self__ instance to which this method is bound"""
return isinstance(object, types.MethodType)
def ismethoddescriptor(object):
......@@ -73,7 +72,7 @@ def ismethoddescriptor(object):
Methods implemented via descriptors that also pass one of the other
tests return false from the ismethoddescriptor() test, simply because
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__")
and not hasattr(object, "__set__") # else it's a data descriptor
and not ismethod(object) # mutual exclusion
......@@ -351,7 +350,7 @@ def getfile(object):
return object.__file__
raise TypeError('arg is a built-in class')
if ismethod(object):
object = object.im_func
object = object.__func__
if isfunction(object):
object = object.__code__
if istraceback(object):
......@@ -494,7 +493,7 @@ def findsource(object):
raise IOError('could not find class definition')
if ismethod(object):
object = object.im_func
object = object.__func__
if isfunction(object):
object = object.__code__
if istraceback(object):
......@@ -744,7 +743,7 @@ def getfullargspec(func):
"""
if ismethod(func):
func = func.im_func
func = func.__func__
if not isfunction(func):
raise TypeError('arg is not a Python function')
args, varargs, kwonlyargs, varkw = _getfullargs(func.__code__)
......
......@@ -1081,7 +1081,7 @@ class Misc:
f = CallWrapper(func, subst, self).__call__
name = repr(id(f))
try:
func = func.im_func
func = func.__func__
except AttributeError:
pass
try:
......
......@@ -345,8 +345,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
except:
func = arg
try:
if hasattr(func, 'im_func'):
func = func.im_func
if hasattr(func, '__func__'):
func = func.__func__
code = func.__code__
#use co_name to identify the bkpt (function names
#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)
return
# Is it an instance method?
try: code = value.im_func.__code__
try: code = value.__func__.__code__
except: pass
if code:
print('Method', code.co_name, file=self.stdout)
......
......@@ -848,17 +848,17 @@ class HTMLDoc(Doc):
note = ''
skipdocs = 0
if inspect.ismethod(object):
imclass = object.im_class
imclass = object.__self__.__class__
if cl:
if imclass is not cl:
note = ' from ' + self.classlink(imclass, mod)
else:
if object.im_self is not None:
if object.__self__ is not None:
note = ' method of %s instance' % self.classlink(
object.im_self.__class__, mod)
object.__self__.__class__, mod)
else:
note = ' unbound %s method' % self.classlink(imclass,mod)
object = object.im_func
object = object.__func__
if name == realname:
title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
......@@ -1227,17 +1227,17 @@ class TextDoc(Doc):
note = ''
skipdocs = 0
if inspect.ismethod(object):
imclass = object.im_class
imclass = object.__self__.__class__
if cl:
if imclass is not cl:
note = ' from ' + classname(imclass, mod)
else:
if object.im_self is not None:
if object.__self__ is not None:
note = ' method of %s instance' % classname(
object.im_self.__class__, mod)
object.__self__.__class__, mod)
else:
note = ' unbound %s method' % classname(imclass,mod)
object = object.im_func
object = object.__func__
if name == realname:
title = self.bold(realname)
......
......@@ -33,6 +33,6 @@ lst = [None] * 1000000
i = 0
del a
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
i += 1
......@@ -280,12 +280,12 @@ def test_dir():
c = C()
vereq(interesting(dir(c)), cstuff)
#verify('im_self' in dir(C.Cmethod))
#verify('__self__' in dir(C.Cmethod))
c.cdata = 2
c.cmethod = lambda self: 0
vereq(interesting(dir(c)), cstuff + ['cdata', 'cmethod'])
#verify('im_self' in dir(c.Cmethod))
#verify('__self__' in dir(c.Cmethod))
class A(C):
Adata = 1
......@@ -293,13 +293,13 @@ def test_dir():
astuff = ['Adata', 'Amethod'] + cstuff
vereq(interesting(dir(A)), astuff)
#verify('im_self' in dir(A.Amethod))
#verify('__self__' in dir(A.Amethod))
a = A()
vereq(interesting(dir(a)), astuff)
a.adata = 42
a.amethod = lambda self: 3
vereq(interesting(dir(a)), astuff + ['adata', 'amethod'])
#verify('im_self' in dir(a.Amethod))
#verify('__self__' in dir(a.Amethod))
# Try a module subclass.
import sys
......@@ -1418,10 +1418,10 @@ def classmethods():
vereq(ff.__get__(0)(42), (int, 42))
# Test super() with classmethods (SF bug 535444)
veris(C.goo.im_self, C)
veris(D.goo.im_self, D)
veris(super(D,D).goo.im_self, D)
veris(super(D,d).goo.im_self, D)
veris(C.goo.__self__, C)
veris(D.goo.__self__, D)
veris(super(D,D).goo.__self__, D)
veris(super(D,d).goo.__self__, D)
vereq(super(D,D).goo(), (D,))
vereq(super(D,d).goo(), (D,))
......@@ -1507,7 +1507,7 @@ def classic():
r = repr(E().foo)
verify(r.startswith("<bound method E.foo "), r)
r = repr(C.foo.__get__(C()))
verify(r.startswith("<bound method ?.foo "), r)
verify(r.startswith("<bound method "), r)
def compattr():
if verbose: print("Testing computed attributes...")
......@@ -1687,7 +1687,7 @@ def methods():
vereq(d2.goo(), 1)
class E(object):
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)))
verify(r.startswith("<bound method "), r)
......@@ -1864,17 +1864,6 @@ def recursions():
## raise TestFailed, "expected a RuntimeError for print recursion"
## 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():
if verbose: print("Testing weak references...")
import weakref
......
......@@ -104,11 +104,12 @@ else: raise TestFailed
if f2.a.one != f1.a.one != F.a.one != 11:
raise TestFailed
# im_func may not be a Python method!
# __func__ may not be a Python method!
import new
F.id = new.instancemethod(id, None, F)
F.id = id
eff = F()
eff.id = new.instancemethod(id, eff)
if eff.id() != id(eff):
raise TestFailed
......@@ -296,32 +297,32 @@ def test_func_dict():
verify(f.__dict__ == {'world': 'hello'})
cantset(f, "__dict__", None)
def test_im_class():
def test___self__():
class C:
def foo(self): pass
#verify(C.foo.im_class is C)
verify(C().foo.im_class is C)
#cantset(C.foo, "im_class", C)
cantset(C().foo, "im_class", C)
#verify(C.foo.__self__.__class__ is C)
verify(C().foo.__self__.__class__ is C)
#cantset(C.foo, "__self__.__class__", C)
cantset(C().foo, "__self__.__class__", C)
def test_im_func():
def test___func__():
def foo(self): pass
class C:
pass
C.foo = foo
#verify(C.foo.im_func is foo)
verify(C().foo.im_func is foo)
#cantset(C.foo, "im_func", foo)
cantset(C().foo, "im_func", foo)
#verify(C.foo.__func__ is foo)
verify(C().foo.__func__ is foo)
#cantset(C.foo, "__func__", foo)
cantset(C().foo, "__func__", foo)
def test_im_self():
def test___self__():
class C:
def foo(self): pass
#verify(C.foo.im_self is None)
#verify(C.foo.__self__ is None)
c = C()
#verify(c.foo.im_self is c)
#cantset(C.foo, "im_self", None)
#cantset(c.foo, "im_self", c)
#verify(c.foo.__self__ is c)
#cantset(C.foo, "__self__", None)
#cantset(c.foo, "__self__", c)
def test_im_dict():
class C:
......@@ -358,9 +359,9 @@ def testmore():
test_func_defaults()
test_func_dict()
# Tests for instance method attributes
test_im_class()
test_im_func()
test_im_self()
test___self__()
test___func__()
test___self__()
test_im_dict()
test_im_doc()
test_im_name()
......
......@@ -25,7 +25,7 @@ class NewTest(unittest.TestCase):
# new.instancemethod()
c = C()
c.yolks = 3
im = new.instancemethod(break_yolks, c, C)
im = new.instancemethod(break_yolks, c)
self.assertEqual(c.get_yolks(), 3,
'Broken call of hand-crafted class instance')
......@@ -43,7 +43,7 @@ class NewTest(unittest.TestCase):
self.assertEqual(c.get_yolks(), -1)
# 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
self.assertRaises(TypeError, new.instancemethod, break_yolks, c, kw=1)
......
......@@ -31,7 +31,7 @@ class HookWatcher:
def get_events(self):
"""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
return [item for item in self.events if item[2] not in disallowed]
......
......@@ -67,7 +67,7 @@ class PyclbrTest(TestCase):
if isinstance(obj, MethodType):
# could be a classmethod
if (not isinstance(classdict[name], ClassMethodType) or
obj.im_self is not oclass):
obj.__self__ is not oclass):
return False
elif not isinstance(obj, FunctionType):
return False
......
......@@ -44,6 +44,12 @@ Core and Builtins
- Renamed structmember.h WRITE_RESTRICTED to PY_WRITE_RESTRICTED to work
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
-----------------
......
......@@ -26,33 +26,25 @@ PyMethod_Self(PyObject *im)
return ((PyMethodObject *)im)->im_self;
}
PyObject *
PyMethod_Class(PyObject *im)
{
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
/* Method objects are used for bound instance methods returned by
instancename.methodname. ClassName.methodname returns an ordinary
function.
*/
static PyMethodObject *free_list;
PyObject *
PyMethod_New(PyObject *func, PyObject *self, PyObject *klass)
PyMethod_New(PyObject *func, PyObject *self)
{
register PyMethodObject *im;
if (!PyCallable_Check(func)) {
PyErr_BadInternalCall();
return NULL;
}
if (self == NULL) {
PyErr_BadInternalCall();
return NULL;
}
im = free_list;
if (im != NULL) {
free_list = (PyMethodObject *)(im->im_self);
......@@ -68,25 +60,21 @@ PyMethod_New(PyObject *func, PyObject *self, PyObject *klass)
im->im_func = func;
Py_XINCREF(self);
im->im_self = self;
Py_XINCREF(klass);
im->im_class = klass;
_PyObject_GC_TRACK(im);
return (PyObject *)im;
}
/* 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)
static PyMemberDef method_memberlist[] = {
{"im_class", T_OBJECT, OFF(im_class), READONLY|RESTRICTED,
"the class associated with a method"},
{"im_func", T_OBJECT, OFF(im_func), READONLY|RESTRICTED,
{"__func__", T_OBJECT, OFF(im_func), READONLY|RESTRICTED,
"the function (or other callable) implementing a method"},
{"im_self", T_OBJECT, OFF(im_self), READONLY|RESTRICTED,
"the instance to which a method is bound; None for unbound methods"},
{"__self__", T_OBJECT, OFF(im_self), READONLY|RESTRICTED,
"the instance to which a method is bound"},
{NULL} /* Sentinel */
};
......@@ -141,7 +129,7 @@ method_getattro(PyObject *obj, PyObject *name)
}
PyDoc_STRVAR(method_doc,
"method(function, instance, class)\n\
"method(function, instance)\n\
\n\
Create an instance method object.");
......@@ -150,27 +138,24 @@ method_new(PyTypeObject* type, PyObject* args, PyObject *kw)
{
PyObject *func;
PyObject *self;
PyObject *classObj = NULL;
if (!_PyArg_NoKeywords("instancemethod", kw))
return NULL;
if (!PyArg_UnpackTuple(args, "method", 2, 3,
&func, &self, &classObj))
&func, &self))
return NULL;
if (!PyCallable_Check(func)) {
PyErr_SetString(PyExc_TypeError,
"first argument must be callable");
return NULL;
}
if (self == Py_None)
self = NULL;
if (self == NULL && classObj == NULL) {
if (self == NULL || self == Py_None) {
PyErr_SetString(PyExc_TypeError,
"unbound methods must have non-NULL im_class");
"self must not be None");
return NULL;
}
return PyMethod_New(func, self, classObj);
return PyMethod_New(func, self);
}
static void
......@@ -181,7 +166,6 @@ method_dealloc(register PyMethodObject *im)
PyObject_ClearWeakRefs((PyObject *)im);
Py_DECREF(im->im_func);
Py_XDECREF(im->im_self);
Py_XDECREF(im->im_class);
im->im_self = (PyObject *)free_list;
free_list = im;
}
......@@ -225,10 +209,15 @@ method_repr(PyMethodObject *a)
{
PyObject *self = a->im_self;
PyObject *func = a->im_func;
PyObject *klass = a->im_class;
PyObject *funcname = NULL, *klassname = NULL, *result = NULL;
PyObject *klass = (PyObject*)Py_Type(self);
PyObject *funcname = NULL ,*klassname = NULL, *result = NULL;
char *defname = "?";
if (self == NULL) {
PyErr_BadInternalCall();
return NULL;
}
funcname = PyObject_GetAttrString(func, "__name__");
if (funcname == NULL) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
......@@ -239,6 +228,7 @@ method_repr(PyMethodObject *a)
Py_DECREF(funcname);
funcname = NULL;
}
if (klass == NULL)
klassname = NULL;
else {
......@@ -253,16 +243,12 @@ method_repr(PyMethodObject *a)
klassname = NULL;
}
}
if (self == NULL)
result = PyUnicode_FromFormat("<unbound method %V.%V>",
klassname, defname,
funcname, defname);
else {
/* XXX Shouldn't use repr()/%R here! */
result = PyUnicode_FromFormat("<bound method %V.%V of %R>",
klassname, defname,
funcname, defname, self);
}
/* XXX Shouldn't use repr()/%R here! */
result = PyUnicode_FromFormat("<bound method %V.%V of %R>",
klassname, defname,
funcname, defname, self);
Py_XDECREF(funcname);
Py_XDECREF(klassname);
return result;
......@@ -292,92 +278,19 @@ method_traverse(PyMethodObject *im, visitproc visit, void *arg)
{
Py_VISIT(im->im_func);
Py_VISIT(im->im_self);
Py_VISIT(im->im_class);
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 *
method_call(PyObject *func, PyObject *arg, PyObject *kw)
{
PyObject *self = PyMethod_GET_SELF(func);
PyObject *klass = PyMethod_GET_CLASS(func);
PyObject *result;
func = PyMethod_GET_FUNCTION(func);
if (self == NULL) {
/* Unbound methods must be called with an instance of
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;
}
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);
PyErr_BadInternalCall();
return NULL;
}
else {
Py_ssize_t argcount = PyTuple_Size(arg);
......@@ -402,27 +315,15 @@ method_call(PyObject *func, PyObject *arg, PyObject *kw)
static PyObject *
method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls)
{
/* Don't rebind an already bound method, or an unbound method
of a class that's not a base class of cls. */
/* Don't rebind an already bound method of a class that's not a base
class of cls. */
if (PyMethod_GET_SELF(meth) != NULL) {
/* Already bound */
Py_INCREF(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 */
return PyMethod_New(PyMethod_GET_FUNCTION(meth), obj, cls);
return PyMethod_New(PyMethod_GET_FUNCTION(meth), obj);
}
PyTypeObject PyMethod_Type = {
......
......@@ -647,7 +647,7 @@ func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
Py_INCREF(func);
return func;
}
return PyMethod_New(func, obj, type);
return PyMethod_New(func, obj);
}
PyTypeObject PyFunction_Type = {
......@@ -751,8 +751,7 @@ cm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
}
if (type == NULL)
type = (PyObject *)(Py_Type(obj));
return PyMethod_New(cm->cm_callable,
type, (PyObject *)(Py_Type(type)));
return PyMethod_New(cm->cm_callable, type);
}
static int
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment