[Python-checkins] bpo-43682: Make staticmethod objects callable (GH-25117)

vstinner webhook-mailer at python.org
Sun Apr 11 18:21:28 EDT 2021


https://github.com/python/cpython/commit/553ee2781a37ac9d2068da3e1325a780ca79e21e
commit: 553ee2781a37ac9d2068da3e1325a780ca79e21e
branch: master
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-04-12T00:21:22+02:00
summary:

bpo-43682: Make staticmethod objects callable (GH-25117)

Static methods (@staticmethod) are now callable as regular functions.

files:
A Misc/NEWS.d/next/Core and Builtins/2021-03-31-16-32-57.bpo-43682.VSF3vg.rst
M Doc/library/functions.rst
M Doc/reference/datamodel.rst
M Doc/whatsnew/3.10.rst
M Lib/test/test_decorators.py
M Lib/test/test_pydoc.py
M Objects/funcobject.c

diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index dca8b9334877d..30f62e69e7fb9 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 9c819b7ae6d28..3b8780d834bf0 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 50c8d53e57d83..9f6b7a469ca49 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 7d0243ab19939..d4353457933f3 100644
--- a/Lib/test/test_decorators.py
+++ b/Lib/test/test_decorators.py
@@ -91,14 +91,18 @@ def func(x):
                           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 e94ebd30160e0..9bde0c75bc906 100644
--- a/Lib/test/test_pydoc.py
+++ b/Lib/test/test_pydoc.py
@@ -1142,7 +1142,7 @@ def sm(x, y):
                 '''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 0000000000000..1ad949389a1f6
--- /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 df59131912190..f0b0b673d4fa4 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 */



More information about the Python-checkins mailing list