]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-43682: Make staticmethod objects callable (GH-25117)
authorVictor Stinner <vstinner@python.org>
Sun, 11 Apr 2021 22:21:22 +0000 (00:21 +0200)
committerGitHub <noreply@github.com>
Sun, 11 Apr 2021 22:21:22 +0000 (00:21 +0200)
Static methods (@staticmethod) are now callable as regular functions.

Doc/library/functions.rst
Doc/reference/datamodel.rst
Doc/whatsnew/3.10.rst
Lib/test/test_decorators.py
Lib/test/test_pydoc.py
Misc/NEWS.d/next/Core and Builtins/2021-03-31-16-32-57.bpo-43682.VSF3vg.rst [new file with mode: 0644]
Objects/funcobject.c

index dca8b9334877d68318e6b697db3a1129d4b4c863..30f62e69e7fb9f7f69f0b8fb2f1cac492671642c 100644 (file)
@@ -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::
index 9c819b7ae6d28f9b1b6b3609c3c17e8b7f39c7d5..3b8780d834bf0ce4e08b8678aee9ab1c4c25589c 100644 (file)
@@ -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
index 50c8d53e57d83593b61683b2e2a99a0f24a77183..9f6b7a469ca4920a27cf3ddd5d893b1cf3042d0e 100644 (file)
@@ -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`.)
 
 
index 7d0243ab199393b37d526adf35fa683f4ad9327e..d4353457933f3db890dedd84a1032631d6f4c2ff 100644 (file)
@@ -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()
index e94ebd30160e02346a6aaf83206c70425637d155..9bde0c75bc9067c71c8d46785e5b935e28dff5da 100644 (file)
@@ -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 (file)
index 0000000..1ad9493
--- /dev/null
@@ -0,0 +1,2 @@
+Static methods (:func:`@staticmethod <staticmethod>`) are now callable as
+regular functions. Patch by Victor Stinner.
index df59131912190bbf80fb4eedac4dd9019d551db2..f0b0b673d4fa404ffada6dc636d29c66ff48714e 100644 (file)
@@ -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 */