diff options
author | Christian Heimes <christian@cheimes.de> | 2007-11-27 10:40:20 (GMT) |
---|---|---|
committer | Christian Heimes <christian@cheimes.de> | 2007-11-27 10:40:20 (GMT) |
commit | ff737954f3ee3005236133fc51b55a508b11aa06 (patch) | |
tree | b65ae9e39e774bd73674b5088e549d09a7bfd7d6 | |
parent | 0d3fb8a944a810f421377d5823cbc006700b3c1d (diff) | |
download | cpython-ff737954f3ee3005236133fc51b55a508b11aa06.zip cpython-ff737954f3ee3005236133fc51b55a508b11aa06.tar.gz cpython-ff737954f3ee3005236133fc51b55a508b11aa06.tar.bz2 |
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.
-rw-r--r-- | Doc/library/inspect.rst | 9 | ||||
-rw-r--r-- | Doc/library/new.rst | 6 | ||||
-rw-r--r-- | Doc/library/stdtypes.rst | 14 | ||||
-rw-r--r-- | Doc/reference/datamodel.rst | 55 | ||||
-rw-r--r-- | Doc/tutorial/classes.rst | 4 | ||||
-rw-r--r-- | Include/classobject.h | 9 | ||||
-rw-r--r-- | Lib/ctypes/test/test_callbacks.py | 8 | ||||
-rw-r--r-- | Lib/dis.py | 4 | ||||
-rw-r--r-- | Lib/doctest.py | 4 | ||||
-rw-r--r-- | Lib/idlelib/CallTips.py | 6 | ||||
-rw-r--r-- | Lib/inspect.py | 13 | ||||
-rw-r--r-- | Lib/lib-tk/Tkinter.py | 2 | ||||
-rwxr-xr-x | Lib/pdb.py | 6 | ||||
-rwxr-xr-x | Lib/pydoc.py | 16 | ||||
-rw-r--r-- | Lib/test/crashers/borrowed_ref_2.py | 2 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 31 | ||||
-rw-r--r-- | Lib/test/test_funcattrs.py | 41 | ||||
-rw-r--r-- | Lib/test/test_new.py | 4 | ||||
-rw-r--r-- | Lib/test/test_profilehooks.py | 2 | ||||
-rw-r--r-- | Lib/test/test_pyclbr.py | 2 | ||||
-rw-r--r-- | Misc/NEWS | 6 | ||||
-rw-r--r-- | Objects/classobject.c | 171 | ||||
-rw-r--r-- | Objects/funcobject.c | 5 |
23 files changed, 152 insertions, 268 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index cf14de9..988b737 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -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) diff --git a/Doc/library/new.rst b/Doc/library/new.rst index 6153ff1..6c5a4bf 100644 --- a/Doc/library/new.rst +++ b/Doc/library/new.rst @@ -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]]]) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index e6f7e7b..1e81ed9 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -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. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 1e85f83..75cb52f 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -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 diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index ef6498e..4e95419 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -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. diff --git a/Include/classobject.h b/Include/classobject.h index 885c43e..e6ca421 100644 --- a/Include/classobject.h +++ b/Include/classobject.h @@ -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) */ @@ -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 } diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index d870eb4..bf580ae 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -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__'): diff --git a/Lib/doctest.py b/Lib/doctest.py index 5b18073..f135027 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -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 diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py index aee7e61..cda2be9 100644 --- a/Lib/idlelib/CallTips.py +++ b/Lib/idlelib/CallTips.py @@ -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) diff --git a/Lib/inspect.py b/Lib/inspect.py index 3a95796..a1910e4 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -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__) diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py index f40a553..aecb317 100644 --- a/Lib/lib-tk/Tkinter.py +++ b/Lib/lib-tk/Tkinter.py @@ -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) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 3cffa06..51d627e 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -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) diff --git a/Lib/test/crashers/borrowed_ref_2.py b/Lib/test/crashers/borrowed_ref_2.py index f3ca350..6e403eb 100644 --- a/Lib/test/crashers/borrowed_ref_2.py +++ b/Lib/test/crashers/borrowed_ref_2.py @@ -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 diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index e093ce8..a518f16 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -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 diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index b9b2e6e..3d0d4aa 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -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() diff --git a/Lib/test/test_new.py b/Lib/test/test_new.py index 797a8c3..8e1d49d 100644 --- a/Lib/test/test_new.py +++ b/Lib/test/test_new.py @@ -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) diff --git a/Lib/test/test_profilehooks.py b/Lib/test/test_profilehooks.py index 0f5616d..3a17dc7 100644 --- a/Lib/test/test_profilehooks.py +++ b/Lib/test/test_profilehooks.py @@ -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] diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index bcb7988..b88cb7e 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -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 ----------------- diff --git a/Objects/classobject.c b/Objects/classobject.c index 2db898a..5362fbc 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -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 = { diff --git a/Objects/funcobject.c b/Objects/funcobject.c index f9b0346..ac68edc 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -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 |