diff options
-rw-r--r-- | Doc/api/newtypes.tex | 32 | ||||
-rw-r--r-- | Include/methodobject.h | 8 | ||||
-rw-r--r-- | Objects/methodobject.c | 2 | ||||
-rw-r--r-- | Objects/typeobject.c | 31 | ||||
-rw-r--r-- | Python/modsupport.c | 7 |
5 files changed, 74 insertions, 6 deletions
diff --git a/Doc/api/newtypes.tex b/Doc/api/newtypes.tex index 8ab8e3c..b4f90e5 100644 --- a/Doc/api/newtypes.tex +++ b/Doc/api/newtypes.tex @@ -152,8 +152,12 @@ a cast in the method table. Even though \ctype{PyCFunction} defines the first parameter as \ctype{PyObject*}, it is common that the method implementation uses a the specific C type of the \var{self} object. -The flags can have the following values. Only \constant{METH_VARARGS} -and \constant{METH_KEYWORDS} can be combined; the others can't. +The \member{ml_flags} field is a bitfield which can include the +following flags. The individual flags indicate either a calling +convention or a binding convention. Of the calling convention flags, +only \constant{METH_VARARGS} and \constant{METH_KEYWORDS} can be +combined. Any of the calling convention flags can be combined with a +binding flag. \begin{datadesc}{METH_VARARGS} This is the typical calling convention, where the methods have the @@ -203,6 +207,30 @@ and \constant{METH_KEYWORDS} can be combined; the others can't. argument. \end{datadesc} +These two constants are not used to indicate the calling convention +but the binding when use with methods of classes. These may not be +used for functions defined for modules. At most one of these flags +may be set for any given method. + +\begin{datadesc}{METH_CLASS} + The method will be passed the type object as the first parameter + rather than an instance of the type. This is used to create + \emph{class methods}, similar to what is created when using the + \function{classmethod()}\bifuncindex{classmethod} built-in + function. + \versionadded{2.3} +\end{datadesc} + +\begin{datadesc}{METH_STATIC} + The method will be passed \NULL{} as the first parameter rather than + an instance of the type. This is used to create \emph{static + methods}, similar to what is created when using the + \function{staticmethod()}\bifuncindex{staticmethod} built-in + function. + \versionadded{2.3} +\end{datadesc} + + \begin{cfuncdesc}{PyObject*}{Py_FindMethod}{PyMethodDef table[], PyObject *ob, char *name} Return a bound method object for an extension type implemented in diff --git a/Include/methodobject.h b/Include/methodobject.h index e47ebea..21dcdda 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -46,10 +46,16 @@ extern DL_IMPORT(PyObject *) PyCFunction_New(PyMethodDef *, PyObject *); #define METH_OLDARGS 0x0000 #define METH_VARARGS 0x0001 #define METH_KEYWORDS 0x0002 -/* METH_NOARGS and METH_O must not be combined with any other flag. */ +/* METH_NOARGS and METH_O must not be combined with the flags above. */ #define METH_NOARGS 0x0004 #define METH_O 0x0008 +/* METH_CLASS and METH_STATIC are a little different; these control + the construction of methods for a class. These cannot be used for + functions in modules. */ +#define METH_CLASS 0x0010 +#define METH_STATIC 0x0020 + typedef struct PyMethodChain { PyMethodDef *methods; /* Methods of this type */ struct PyMethodChain *link; /* NULL or base type */ diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 05474b5..0cb4fd8 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -62,7 +62,7 @@ PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) PyCFunctionObject* f = (PyCFunctionObject*)func; PyCFunction meth = PyCFunction_GET_FUNCTION(func); PyObject *self = PyCFunction_GET_SELF(func); - int flags = PyCFunction_GET_FLAGS(func); + int flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC); int size = PyTuple_GET_SIZE(arg); if (flags & METH_KEYWORDS) { diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c938f52..a596721 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1694,6 +1694,20 @@ PyTypeObject PyBaseObject_Type = { /* Initialize the __dict__ in a type object */ +static PyObject * +create_specialmethod(PyMethodDef *meth, PyObject *(*func)(PyObject *)) +{ + PyObject *cfunc; + PyObject *result; + + cfunc = PyCFunction_New(meth, NULL); + if (cfunc == NULL) + return NULL; + result = func(cfunc); + Py_DECREF(cfunc); + return result; +} + static int add_methods(PyTypeObject *type, PyMethodDef *meth) { @@ -1703,10 +1717,23 @@ add_methods(PyTypeObject *type, PyMethodDef *meth) PyObject *descr; if (PyDict_GetItemString(dict, meth->ml_name)) continue; - descr = PyDescr_NewMethod(type, meth); + if (meth->ml_flags & METH_CLASS) { + if (meth->ml_flags & METH_STATIC) { + PyErr_SetString(PyExc_ValueError, + "method cannot be both class and static"); + return -1; + } + descr = create_specialmethod(meth, PyClassMethod_New); + } + else if (meth->ml_flags & METH_STATIC) { + descr = create_specialmethod(meth, PyStaticMethod_New); + } + else { + descr = PyDescr_NewMethod(type, meth); + } if (descr == NULL) return -1; - if (PyDict_SetItemString(dict,meth->ml_name, descr) < 0) + if (PyDict_SetItemString(dict, meth->ml_name, descr) < 0) return -1; Py_DECREF(descr); } diff --git a/Python/modsupport.c b/Python/modsupport.c index 0450a8a..f29b27c 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -57,6 +57,13 @@ Py_InitModule4(char *name, PyMethodDef *methods, char *doc, return NULL; d = PyModule_GetDict(m); for (ml = methods; ml->ml_name != NULL; ml++) { + if ((ml->ml_flags & METH_CLASS) || + (ml->ml_flags & METH_STATIC)) { + PyErr_SetString(PyExc_ValueError, + "module functions cannot set" + " METH_CLASS or METH_STATIC"); + return NULL; + } v = PyCFunction_New(ml, passthrough); if (v == NULL) return NULL; |