Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-144330: Initialize classmethod and staticmethod in new#144469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
vstinner wants to merge2 commits intopython:main
base:main
Choose a base branch
Loading
fromvstinner:cm_new
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletionsLib/test/test_descr.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1727,6 +1727,18 @@ def annotated(cls) -> int: pass
del method.__annotate__
self.assertIs(method.__annotate__, original_annotate)

def test_classmethod_without_dict_access(self):
class Spam:
@classmethod
def method(cls, x, y):
pass

obj = Spam.__dict__['method']
self.assertIsInstance(obj, classmethod)
self.assertEqual(obj.__annotations__, {})
self.assertEqual(obj.__name__, 'method')
self.assertEqual(obj.__module__, __name__)

def test_staticmethod_annotations_without_dict_access(self):
# gh-125017: this used to crash
class Spam:
Expand All@@ -1737,15 +1749,8 @@ def __new__(cls, x, y):
obj = Spam.__dict__['__new__']
self.assertIsInstance(obj, staticmethod)
self.assertEqual(obj.__annotations__, {})

@support.refcount_test
def test_refleaks_in_classmethod___init__(self):
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
cm = classmethod(None)
refs_before = gettotalrefcount()
for i in range(100):
cm.__init__(None)
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
self.assertEqual(obj.__name__, '__new__')
self.assertEqual(obj.__module__, __name__)

@support.impl_detail("the module 'xxsubtype' is internal")
@unittest.skipIf(xxsubtype is None, "requires xxsubtype module")
Expand DownExpand Up@@ -1822,15 +1827,6 @@ class D(C):
del sm.x
self.assertNotHasAttr(sm, "x")

@support.refcount_test
def test_refleaks_in_staticmethod___init__(self):
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
sm = staticmethod(None)
refs_before = gettotalrefcount()
for i in range(100):
sm.__init__(None)
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)

@support.impl_detail("the module 'xxsubtype' is internal")
@unittest.skipIf(xxsubtype is None, "requires xxsubtype module")
def test_staticmethods_in_c(self):
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
Move ``classmethod`` and ``staticmethod`` initialization from ``__init__()``
to ``__new__()``. Patch by Victor Stinner.
130 changes: 95 additions & 35 deletionsObjects/funcobject.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1466,33 +1466,59 @@ static PyObject *
cm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
classmethod *cm = (classmethod *)self;

if (cm->cm_callable == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"uninitialized classmethod object");
return NULL;
}
if (type == NULL)
type = (PyObject *)(Py_TYPE(obj));
return PyMethod_New(cm->cm_callable, type);
}

static int
cm_init(PyObject *self, PyObject *args, PyObject *kwds)
cm_set_callable(classmethod *cm, PyObject *callable)
{
classmethod *cm = (classmethod *)self;
PyObject *callable;
assert(callable != NULL);
if (cm->cm_callable == callable) {
// cm_init() sets the same callable than cm_new()
return 0;
}

if (!_PyArg_NoKeywords("classmethod", kwds))
return -1;
if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable))
return -1;
Py_XSETREF(cm->cm_callable, Py_NewRef(callable));
return functools_wraps((PyObject *)cm, cm->cm_callable);
}

static PyObject *
cm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
if (!_PyArg_NoKeywords("classmethod", kwds)) {
return NULL;
}
PyObject *callable; // borrowed ref
if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable)) {
return NULL;
}

if (functools_wraps((PyObject *)cm, cm->cm_callable) < 0) {
classmethod *cm = (classmethod *)PyType_GenericAlloc(type, 0);
if (cm == NULL) {
return NULL;
}
if (cm_set_callable(cm, callable) < 0) {
Py_DECREF(cm);
return NULL;
}
return (PyObject *)cm;
}

static int
cm_init(PyObject *self, PyObject *args, PyObject *kwds)
{
if (!_PyArg_NoKeywords("classmethod", kwds)) {
return -1;
}
return 0;
PyObject *callable; // borrowed ref
if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable)) {
return -1;
}

classmethod *cm = (classmethod *)self;
return cm_set_callable(cm, callable);
}

static PyMemberDef cm_memberlist[] = {
Expand DownExpand Up@@ -1623,7 +1649,7 @@ PyTypeObject PyClassMethod_Type = {
offsetof(classmethod, cm_dict), /* tp_dictoffset */
cm_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
cm_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};

Expand All@@ -1632,8 +1658,12 @@ PyClassMethod_New(PyObject *callable)
{
classmethod *cm = (classmethod *)
PyType_GenericAlloc(&PyClassMethod_Type, 0);
if (cm != NULL) {
cm->cm_callable = Py_NewRef(callable);
if (cm == NULL) {
return NULL;
}
if (cm_set_callable(cm, callable) < 0) {
Py_DECREF(cm);
return NULL;
}
return (PyObject *)cm;
}
Expand DownExpand Up@@ -1699,31 +1729,57 @@ static PyObject *
sm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
staticmethod *sm = (staticmethod *)self;
return Py_NewRef(sm->sm_callable);
}

if (sm->sm_callable == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"uninitialized staticmethod object");
static int
sm_set_callable(staticmethod *sm, PyObject *callable)
{
assert(callable != NULL);
if (sm->sm_callable == callable) {
// sm_init() sets the same callable than sm_new()
return 0;
}

Py_XSETREF(sm->sm_callable, Py_NewRef(callable));
return functools_wraps((PyObject *)sm, sm->sm_callable);
}

static PyObject *
sm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
if (!_PyArg_NoKeywords("staticmethod", kwds)) {
return NULL;
}
return Py_NewRef(sm->sm_callable);
PyObject *callable; // borrowed ref
if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable)) {
return NULL;
}

staticmethod *sm = (staticmethod *)PyType_GenericAlloc(type, 0);
if (sm == NULL) {
return NULL;
}
if (sm_set_callable(sm, callable) < 0) {
Py_DECREF(sm);
return NULL;
}
return (PyObject *)sm;
}

static int
sm_init(PyObject *self, PyObject *args, PyObject *kwds)
{
staticmethod *sm = (staticmethod *)self;
PyObject *callable;

if (!_PyArg_NoKeywords("staticmethod", kwds))
if (!_PyArg_NoKeywords("staticmethod", kwds)) {
return -1;
if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable))
return -1;
Py_XSETREF(sm->sm_callable, Py_NewRef(callable));

if (functools_wraps((PyObject *)sm, sm->sm_callable) < 0) {
}
PyObject *callable; // borrowed ref
if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable)) {
return -1;
}
return 0;

staticmethod *sm = (staticmethod *)self;
return sm_set_callable(sm, callable);
}

static PyObject*
Expand DownExpand Up@@ -1858,7 +1914,7 @@ PyTypeObject PyStaticMethod_Type = {
offsetof(staticmethod, sm_dict), /* tp_dictoffset */
sm_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
sm_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};

Expand All@@ -1867,8 +1923,12 @@ PyStaticMethod_New(PyObject *callable)
{
staticmethod *sm = (staticmethod *)
PyType_GenericAlloc(&PyStaticMethod_Type, 0);
if (sm != NULL) {
sm->sm_callable = Py_NewRef(callable);
if (sm == NULL) {
return NULL;
}
if (sm_set_callable(sm, callable) < 0) {
Py_DECREF(sm);
return NULL;
}
return (PyObject *)sm;
}
7 changes: 5 additions & 2 deletionsObjects/object.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2446,13 +2446,17 @@ static PyTypeObject* static_types[] = {
&PyBaseObject_Type,
&PyType_Type,

// PyStaticMethod_Type and PyCFunction_Type are used by PyType_Ready()
// on other types and so must be initialized first.
&PyStaticMethod_Type,
&PyCFunction_Type,

// Static types with base=&PyBaseObject_Type
&PyAsyncGen_Type,
&PyByteArrayIter_Type,
&PyByteArray_Type,
&PyBytesIter_Type,
&PyBytes_Type,
&PyCFunction_Type,
&PyCallIter_Type,
&PyCapsule_Type,
&PyCell_Type,
Expand DownExpand Up@@ -2509,7 +2513,6 @@ static PyTypeObject* static_types[] = {
&PySetIter_Type,
&PySet_Type,
&PySlice_Type,
&PyStaticMethod_Type,
&PyStdPrinter_Type,
&PySuper_Type,
&PyTraceBack_Type,
Expand Down
Loading

[8]ページ先頭

©2009-2026 Movatter.jp