summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/functions.rst15
-rw-r--r--Doc/reference/datamodel.rst5
-rw-r--r--Doc/whatsnew/3.10.rst1
-rw-r--r--Lib/test/test_decorators.py12
-rw-r--r--Lib/test/test_pydoc.py2
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2021-03-31-16-32-57.bpo-43682.VSF3vg.rst2
-rw-r--r--Objects/funcobject.c9
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 */