mirror of https://github.com/python/cpython.git
allow arbitrary attributes on classmethod and staticmethod (closes #14051)
This commit is contained in:
parent
dc1d3eda1f
commit
01d7eba316
|
@ -1443,6 +1443,13 @@ def f(cls, arg): return (cls, arg)
|
||||||
else:
|
else:
|
||||||
self.fail("classmethod shouldn't accept keyword args")
|
self.fail("classmethod shouldn't accept keyword args")
|
||||||
|
|
||||||
|
cm = classmethod(f)
|
||||||
|
cm.x = 42
|
||||||
|
self.assertEqual(cm.x, 42)
|
||||||
|
self.assertEqual(cm.__dict__, {"x" : 42})
|
||||||
|
del cm.x
|
||||||
|
self.assertFalse(hasattr(cm, "x"))
|
||||||
|
|
||||||
@support.impl_detail("the module 'xxsubtype' is internal")
|
@support.impl_detail("the module 'xxsubtype' is internal")
|
||||||
def test_classmethods_in_c(self):
|
def test_classmethods_in_c(self):
|
||||||
# Testing C-based class methods...
|
# Testing C-based class methods...
|
||||||
|
@ -1474,6 +1481,12 @@ class D(C):
|
||||||
self.assertEqual(d.goo(1), (1,))
|
self.assertEqual(d.goo(1), (1,))
|
||||||
self.assertEqual(d.foo(1), (d, 1))
|
self.assertEqual(d.foo(1), (d, 1))
|
||||||
self.assertEqual(D.foo(d, 1), (d, 1))
|
self.assertEqual(D.foo(d, 1), (d, 1))
|
||||||
|
sm = staticmethod(None)
|
||||||
|
sm.x = 42
|
||||||
|
self.assertEqual(sm.x, 42)
|
||||||
|
self.assertEqual(sm.__dict__, {"x" : 42})
|
||||||
|
del sm.x
|
||||||
|
self.assertFalse(hasattr(sm, "x"))
|
||||||
|
|
||||||
@support.impl_detail("the module 'xxsubtype' is internal")
|
@support.impl_detail("the module 'xxsubtype' is internal")
|
||||||
def test_staticmethods_in_c(self):
|
def test_staticmethods_in_c(self):
|
||||||
|
|
|
@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #14051: Allow arbitrary attributes to be set of classmethod and
|
||||||
|
staticmethod.
|
||||||
|
|
||||||
- Issue #13020: Fix a reference leak when allocating a structsequence object
|
- Issue #13020: Fix a reference leak when allocating a structsequence object
|
||||||
fails. Patch by Suman Saha.
|
fails. Patch by Suman Saha.
|
||||||
|
|
||||||
|
|
|
@ -754,6 +754,7 @@ PyTypeObject PyFunction_Type = {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
PyObject *cm_callable;
|
PyObject *cm_callable;
|
||||||
|
PyObject *cm_dict;
|
||||||
} classmethod;
|
} classmethod;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -761,6 +762,7 @@ cm_dealloc(classmethod *cm)
|
||||||
{
|
{
|
||||||
_PyObject_GC_UNTRACK((PyObject *)cm);
|
_PyObject_GC_UNTRACK((PyObject *)cm);
|
||||||
Py_XDECREF(cm->cm_callable);
|
Py_XDECREF(cm->cm_callable);
|
||||||
|
Py_XDECREF(cm->cm_dict);
|
||||||
Py_TYPE(cm)->tp_free((PyObject *)cm);
|
Py_TYPE(cm)->tp_free((PyObject *)cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -768,6 +770,7 @@ static int
|
||||||
cm_traverse(classmethod *cm, visitproc visit, void *arg)
|
cm_traverse(classmethod *cm, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
Py_VISIT(cm->cm_callable);
|
Py_VISIT(cm->cm_callable);
|
||||||
|
Py_VISIT(cm->cm_dict);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -775,6 +778,7 @@ static int
|
||||||
cm_clear(classmethod *cm)
|
cm_clear(classmethod *cm)
|
||||||
{
|
{
|
||||||
Py_CLEAR(cm->cm_callable);
|
Py_CLEAR(cm->cm_callable);
|
||||||
|
Py_CLEAR(cm->cm_dict);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,11 +831,19 @@ cm_get___isabstractmethod__(classmethod *cm, void *closure)
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
cm_get___dict__(classmethod *cm, void *closure)
|
||||||
|
{
|
||||||
|
Py_INCREF(cm->cm_dict);
|
||||||
|
return cm->cm_dict;
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef cm_getsetlist[] = {
|
static PyGetSetDef cm_getsetlist[] = {
|
||||||
{"__isabstractmethod__",
|
{"__isabstractmethod__",
|
||||||
(getter)cm_get___isabstractmethod__, NULL,
|
(getter)cm_get___isabstractmethod__, NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL},
|
NULL},
|
||||||
|
{"__dict__", (getter)cm_get___dict__, NULL, NULL, NULL},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -891,7 +903,7 @@ PyTypeObject PyClassMethod_Type = {
|
||||||
0, /* tp_dict */
|
0, /* tp_dict */
|
||||||
cm_descr_get, /* tp_descr_get */
|
cm_descr_get, /* tp_descr_get */
|
||||||
0, /* tp_descr_set */
|
0, /* tp_descr_set */
|
||||||
0, /* tp_dictoffset */
|
offsetof(classmethod, cm_dict), /* tp_dictoffset */
|
||||||
cm_init, /* tp_init */
|
cm_init, /* tp_init */
|
||||||
PyType_GenericAlloc, /* tp_alloc */
|
PyType_GenericAlloc, /* tp_alloc */
|
||||||
PyType_GenericNew, /* tp_new */
|
PyType_GenericNew, /* tp_new */
|
||||||
|
@ -930,6 +942,7 @@ PyClassMethod_New(PyObject *callable)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
PyObject *sm_callable;
|
PyObject *sm_callable;
|
||||||
|
PyObject *sm_dict;
|
||||||
} staticmethod;
|
} staticmethod;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -937,6 +950,7 @@ sm_dealloc(staticmethod *sm)
|
||||||
{
|
{
|
||||||
_PyObject_GC_UNTRACK((PyObject *)sm);
|
_PyObject_GC_UNTRACK((PyObject *)sm);
|
||||||
Py_XDECREF(sm->sm_callable);
|
Py_XDECREF(sm->sm_callable);
|
||||||
|
Py_XDECREF(sm->sm_dict);
|
||||||
Py_TYPE(sm)->tp_free((PyObject *)sm);
|
Py_TYPE(sm)->tp_free((PyObject *)sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,6 +958,7 @@ static int
|
||||||
sm_traverse(staticmethod *sm, visitproc visit, void *arg)
|
sm_traverse(staticmethod *sm, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
Py_VISIT(sm->sm_callable);
|
Py_VISIT(sm->sm_callable);
|
||||||
|
Py_VISIT(sm->sm_dict);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -952,6 +967,7 @@ sm_clear(staticmethod *sm)
|
||||||
{
|
{
|
||||||
Py_XDECREF(sm->sm_callable);
|
Py_XDECREF(sm->sm_callable);
|
||||||
sm->sm_callable = NULL;
|
sm->sm_callable = NULL;
|
||||||
|
Py_CLEAR(sm->sm_dict);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1003,11 +1019,19 @@ sm_get___isabstractmethod__(staticmethod *sm, void *closure)
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
sm_get___dict__(staticmethod *sm, void *closure)
|
||||||
|
{
|
||||||
|
Py_INCREF(sm->sm_dict);
|
||||||
|
return sm->sm_dict;
|
||||||
|
}
|
||||||
|
|
||||||
static PyGetSetDef sm_getsetlist[] = {
|
static PyGetSetDef sm_getsetlist[] = {
|
||||||
{"__isabstractmethod__",
|
{"__isabstractmethod__",
|
||||||
(getter)sm_get___isabstractmethod__, NULL,
|
(getter)sm_get___isabstractmethod__, NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL},
|
NULL},
|
||||||
|
{"__dict__", (getter)sm_get___dict__, NULL, NULL, NULL},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1046,7 +1070,7 @@ PyTypeObject PyStaticMethod_Type = {
|
||||||
0, /* tp_hash */
|
0, /* tp_hash */
|
||||||
0, /* tp_call */
|
0, /* tp_call */
|
||||||
0, /* tp_str */
|
0, /* tp_str */
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
0, /* tp_getattro */
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
||||||
|
@ -1064,7 +1088,7 @@ PyTypeObject PyStaticMethod_Type = {
|
||||||
0, /* tp_dict */
|
0, /* tp_dict */
|
||||||
sm_descr_get, /* tp_descr_get */
|
sm_descr_get, /* tp_descr_get */
|
||||||
0, /* tp_descr_set */
|
0, /* tp_descr_set */
|
||||||
0, /* tp_dictoffset */
|
offsetof(staticmethod, sm_dict), /* tp_dictoffset */
|
||||||
sm_init, /* tp_init */
|
sm_init, /* tp_init */
|
||||||
PyType_GenericAlloc, /* tp_alloc */
|
PyType_GenericAlloc, /* tp_alloc */
|
||||||
PyType_GenericNew, /* tp_new */
|
PyType_GenericNew, /* tp_new */
|
||||||
|
|
Loading…
Reference in New Issue