diff --git a/Include/object.h b/Include/object.h --- a/Include/object.h +++ b/Include/object.h @@ -492,6 +492,9 @@ PyAPI_FUNC(unsigned int) PyType_ClearCache(void); PyAPI_FUNC(void) PyType_Modified(PyTypeObject *); +PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc); +PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc); + /* Generic operations on objects */ struct _Py_Identifier; #ifndef Py_LIMITED_API diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1419,9 +1419,11 @@ _WrapperDescriptor = type(type.__call__) _MethodWrapper = type(all.__call__) +_ClassMethodWrapper = type(int.__dict__['from_bytes']) _NonUserDefinedCallables = (_WrapperDescriptor, _MethodWrapper, + _ClassMethodWrapper, types.BuiltinFunctionType) @@ -1460,12 +1462,13 @@ if sig is not None: return sig - if isinstance(obj, types.FunctionType): return Signature.from_function(obj) - if isinstance(obj, types.BuiltinFunctionType): - return Signature.from_builtin(obj) + if isinstance(obj, _NonUserDefinedCallables) or ismethoddescriptor(obj): + sig = Signature.from_builtin(obj) + if sig: + return sig if isinstance(obj, functools.partial): sig = signature(obj.func) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1595,7 +1595,8 @@ "Signature information for builtins requires docstrings") def test_signature_on_builtins(self): # min doesn't have a signature (yet) - self.assertEqual(inspect.signature(min), None) + with self.assertRaises(ValueError): + inspect.signature(min) signature = inspect.signature(_testcapi.docstring_with_signature_with_defaults) self.assertTrue(isinstance(signature, inspect.Signature)) diff --git a/Objects/descrobject.c b/Objects/descrobject.c --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -350,14 +350,21 @@ return result; } + static PyObject * method_get_doc(PyMethodDescrObject *descr, void *closure) { - if (descr->d_method->ml_doc == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyUnicode_FromString(descr->d_method->ml_doc); + const char *name = descr->d_method->ml_name; + const char *doc = descr->d_method->ml_doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} + +static PyObject * +method_get_text_signature(PyMethodDescrObject *descr, void *closure) +{ + const char *name = descr->d_method->ml_name; + const char *doc = descr->d_method->ml_doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyObject * @@ -425,22 +432,30 @@ static PyGetSetDef method_getset[] = { {"__doc__", (getter)method_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__text_signature__", (getter)method_get_text_signature}, {0} }; static PyObject * member_get_doc(PyMemberDescrObject *descr, void *closure) { - if (descr->d_member->doc == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyUnicode_FromString(descr->d_member->doc); + const char *name = descr->d_member->name; + const char *doc = descr->d_member->doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} + +static PyObject * +member_get_text_signature(PyMemberDescrObject *descr, void *closure) +{ + const char *name = descr->d_member->name; + const char *doc = descr->d_member->doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyGetSetDef member_getset[] = { {"__doc__", (getter)member_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__text_signature__", (getter)member_get_text_signature}, {0} }; @@ -463,16 +478,23 @@ static PyObject * wrapperdescr_get_doc(PyWrapperDescrObject *descr, void *closure) { - if (descr->d_base->doc == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyUnicode_FromString(descr->d_base->doc); + const char *name = descr->d_base->name; + const char *doc = descr->d_base->doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} + +static PyObject * +wrapperdescr_get_text_signature(PyWrapperDescrObject *descr, void *closure) +{ + const char *name = descr->d_base->name; + const char *doc = descr->d_base->doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyGetSetDef wrapperdescr_getset[] = { {"__doc__", (getter)wrapperdescr_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__text_signature__", (getter)wrapperdescr_get_text_signature}, {0} }; @@ -1143,19 +1165,23 @@ } static PyObject * -wrapper_doc(wrapperobject *wp) +wrapper_doc(wrapperobject *wp, void *closure) { - const char *s = wp->descr->d_base->doc; + const char *name = wp->descr->d_base->name; + const char *doc = wp->descr->d_base->doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} - if (s == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - else { - return PyUnicode_FromString(s); - } +static PyObject * +wrapper_text_signature(wrapperobject *wp, void *closure) +{ + const char *name = wp->descr->d_base->name; + const char *doc = wp->descr->d_base->doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } + + static PyObject * wrapper_qualname(wrapperobject *wp) { @@ -1167,6 +1193,7 @@ {"__name__", (getter)wrapper_name}, {"__qualname__", (getter)wrapper_qualname}, {"__doc__", (getter)wrapper_doc}, + {"__text_signature__", (getter)wrapper_text_signature}, {0} }; diff --git a/Objects/methodobject.c b/Objects/methodobject.c --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -179,75 +179,20 @@ {NULL, NULL} }; -/* - * finds the docstring's introspection signature. - * if present, returns a pointer pointing to the first '('. - * otherwise returns NULL. - */ -static const char *find_signature(PyCFunctionObject *m) -{ - const char *trace = m->m_ml->ml_doc; - const char *name = m->m_ml->ml_name; - size_t length; - if (!trace || !name) - return NULL; - length = strlen(name); - if (strncmp(trace, name, length)) - return NULL; - trace += length; - if (*trace != '(') - return NULL; - return trace; -} - -/* - * skips to the end of the docstring's instrospection signature. - */ -static const char *skip_signature(const char *trace) -{ - while (*trace && *trace != '\n') - trace++; - return trace; -} - -static const char *skip_eols(const char *trace) -{ - while (*trace == '\n') - trace++; - return trace; -} - static PyObject * meth_get__text_signature__(PyCFunctionObject *m, void *closure) { - const char *start = find_signature(m); - const char *trace; - - if (!start) { - Py_INCREF(Py_None); - return Py_None; - } - - trace = skip_signature(start); - return PyUnicode_FromStringAndSize(start, trace - start); + const char *name = m->m_ml->ml_name; + const char *doc = m->m_ml->ml_doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyObject * meth_get__doc__(PyCFunctionObject *m, void *closure) { - const char *doc = find_signature(m); - - if (doc) - doc = skip_eols(skip_signature(doc)); - else - doc = m->m_ml->ml_doc; - - if (!doc) { - Py_INCREF(Py_None); - return Py_None; - } - - return PyUnicode_FromString(doc); + const char *name = m->m_ml->ml_name; + const char *doc = m->m_ml->ml_doc; + return _PyType_GetDocFromInternalDoc(name, doc); } static PyObject * diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -54,6 +54,80 @@ static PyObject * slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +/* + * finds the docstring's introspection signature. + * if present, returns a pointer pointing to the first '('. + * otherwise returns NULL. + */ +static const char * +find_signature(const char *name, const char *doc) +{ + size_t length; + if (!doc || !name) + return NULL; + length = strlen(name); + if (strncmp(doc, name, length)) + return NULL; + doc += length; + if (*doc != '(') + return NULL; + return doc; +} + +/* + * skips to the end of the docstring's instrospection signature. + */ +static const char * +skip_signature(const char *doc) +{ + while (*doc && *doc != '\n') + doc++; + return doc; +} + +static const char * +skip_eols(const char *trace) +{ + while (*trace == '\n') + trace++; + return trace; +} + +PyObject * +_PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc) +{ + const char *signature = find_signature(name, internal_doc); + const char *doc; + + if (signature) + doc = skip_eols(skip_signature(signature)); + else + doc = internal_doc; + + if (!doc) { + Py_INCREF(Py_None); + return Py_None; + } + + return PyUnicode_FromString(doc); +} + +PyObject * +_PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc) +{ + const char *signature = find_signature(name, internal_doc); + const char *doc; + + if (!signature) { + Py_INCREF(Py_None); + return Py_None; + } + + doc = skip_signature(signature); + return PyUnicode_FromStringAndSize(signature, doc - signature); +} + unsigned int PyType_ClearCache(void) { @@ -3480,7 +3554,7 @@ { PyObject **dict; dict = _PyObject_GetDictPtr(obj); - /* It is possible that the object's dict is not initialized + /* It is possible that the object's dict is not initialized yet. In this case, we will return None for the state. We also return None if the dict is empty to make the behavior consistent regardless whether the dict was initialized or not. @@ -3788,7 +3862,7 @@ Py_DECREF(state); Py_DECREF(listitems); Py_DECREF(dictitems); - return result; + return result; } static PyObject * @@ -3813,7 +3887,7 @@ } else if (kwargs != NULL) { if (PyDict_Size(kwargs) > 0) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "must use protocol 4 or greater to copy this " "object; since __getnewargs_ex__ returned " "keyword arguments.");