diff options
-rw-r--r-- | Doc/library/functions.rst | 15 | ||||
-rw-r--r-- | Doc/reference/datamodel.rst | 5 | ||||
-rw-r--r-- | Doc/whatsnew/3.10.rst | 1 | ||||
-rw-r--r-- | Lib/test/test_decorators.py | 12 | ||||
-rw-r--r-- | Lib/test/test_pydoc.py | 2 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2021-03-31-16-32-57.bpo-43682.VSF3vg.rst | 2 | ||||
-rw-r--r-- | Objects/funcobject.c | 9 |
7 files changed, 32 insertions, 14 deletions
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index dca8b93..30f62e6 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1619,8 +1619,9 @@ are always available. They are listed here in alphabetical order. The ``@staticmethod`` form is a function :term:`decorator` -- see :ref:`function` for details. - A static method can be called either on the class (such as ``C.f()``) or on an instance (such - as ``C().f()``). + A static method can be called either on the class (such as ``C.f()``) or on + an instance (such as ``C().f()``). Moreover, they can be called as regular + functions (such as ``f()``). Static methods in Python are similar to those found in Java or C++. Also see :func:`classmethod` for a variant that is useful for creating alternate class @@ -1632,15 +1633,19 @@ are always available. They are listed here in alphabetical order. body and you want to avoid the automatic transformation to instance method. For these cases, use this idiom:: + def regular_function(): + ... + class C: - builtin_open = staticmethod(open) + method = staticmethod(regular_function) For more information on static methods, see :ref:`types`. .. versionchanged:: 3.10 Static methods now inherit the method attributes (``__module__``, - ``__name__``, ``__qualname__``, ``__doc__`` and ``__annotations__``) and - have a new ``__wrapped__`` attribute. + ``__name__``, ``__qualname__``, ``__doc__`` and ``__annotations__``), + have a new ``__wrapped__`` attribute, and are now callable as regular + functions. .. index:: diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 9c819b7..3b8780d 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1132,9 +1132,8 @@ Internal types around any other object, usually a user-defined method object. When a static method object is retrieved from a class or a class instance, the object actually returned is the wrapped object, which is not subject to any further - transformation. Static method objects are not themselves callable, although the - objects they wrap usually are. Static method objects are created by the built-in - :func:`staticmethod` constructor. + transformation. Static method objects are also callable. Static method + objects are created by the built-in :func:`staticmethod` constructor. Class method objects A class method object, like a static method object, is a wrapper around another diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 50c8d53..9f6b7a4 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -623,6 +623,7 @@ Other Language Changes (:func:`@classmethod <classmethod>`) now inherit the method attributes (``__module__``, ``__name__``, ``__qualname__``, ``__doc__``, ``__annotations__``) and have a new ``__wrapped__`` attribute. + Moreover, static methods are now callable as regular functions. (Contributed by Victor Stinner in :issue:`43682`.) diff --git a/Lib/test/test_decorators.py b/Lib/test/test_decorators.py index 7d0243a..d435345 100644 --- a/Lib/test/test_decorators.py +++ b/Lib/test/test_decorators.py @@ -91,14 +91,18 @@ class TestDecorators(unittest.TestCase): getattr(func, attr)) self.assertEqual(repr(wrapper), format_str.format(func)) - - self.assertRaises(TypeError, wrapper, 1) + return wrapper def test_staticmethod(self): - self.check_wrapper_attrs(staticmethod, '<staticmethod({!r})>') + wrapper = self.check_wrapper_attrs(staticmethod, '<staticmethod({!r})>') + + # bpo-43682: Static methods are callable since Python 3.10 + self.assertEqual(wrapper(1), 1) def test_classmethod(self): - self.check_wrapper_attrs(classmethod, '<classmethod({!r})>') + wrapper = self.check_wrapper_attrs(classmethod, '<classmethod({!r})>') + + self.assertRaises(TypeError, wrapper, 1) def test_dotted(self): decorators = MiscDecorators() diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index e94ebd3..9bde0c7 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -1142,7 +1142,7 @@ class TestDescriptions(unittest.TestCase): '''A static method''' ... self.assertEqual(self._get_summary_lines(X.__dict__['sm']), - 'sm(...)\n' + 'sm(x, y)\n' ' A static method\n') self.assertEqual(self._get_summary_lines(X.sm), """\ sm(x, y) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-03-31-16-32-57.bpo-43682.VSF3vg.rst b/Misc/NEWS.d/next/Core and Builtins/2021-03-31-16-32-57.bpo-43682.VSF3vg.rst new file mode 100644 index 0000000..1ad9493 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-03-31-16-32-57.bpo-43682.VSF3vg.rst @@ -0,0 +1,2 @@ +Static methods (:func:`@staticmethod <staticmethod>`) are now callable as +regular functions. Patch by Victor Stinner. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index df59131..f0b0b67 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1040,6 +1040,13 @@ sm_init(PyObject *self, PyObject *args, PyObject *kwds) return 0; } +static PyObject* +sm_call(PyObject *callable, PyObject *args, PyObject *kwargs) +{ + staticmethod *sm = (staticmethod *)callable; + return PyObject_Call(sm->sm_callable, args, kwargs); +} + static PyMemberDef sm_memberlist[] = { {"__func__", T_OBJECT, offsetof(staticmethod, sm_callable), READONLY}, {"__wrapped__", T_OBJECT, offsetof(staticmethod, sm_callable), READONLY}, @@ -1107,7 +1114,7 @@ PyTypeObject PyStaticMethod_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - 0, /* tp_call */ + sm_call, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ |