summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorJeroen Demeyer <jeroen.k.demeyer@gmail.com>2019-11-05 15:48:04 (GMT)
committerMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-11-05 15:48:04 (GMT)
commitbf17d41826a8bb4bc1e34ba6345da98aac779e41 (patch)
tree1c256d14c23ffdcbbb5ae54efa546cf718a8f892 /Objects
parentb3966639d28313809774ca3859a347b9007be8d2 (diff)
downloadcpython-bf17d41826a8bb4bc1e34ba6345da98aac779e41.zip
cpython-bf17d41826a8bb4bc1e34ba6345da98aac779e41.tar.gz
cpython-bf17d41826a8bb4bc1e34ba6345da98aac779e41.tar.bz2
bpo-37645: add new function _PyObject_FunctionStr() (GH-14890)
Additional note: the `method_check_args` function in `Objects/descrobject.c` is written in such a way that it applies to all kinds of descriptors. In particular, a future re-implementation of `wrapper_descriptor` could use that code. CC @vstinner @encukou https://bugs.python.org/issue37645 Automerge-Triggered-By: @encukou
Diffstat (limited to 'Objects')
-rw-r--r--Objects/descrobject.c57
-rw-r--r--Objects/methodobject.c37
-rw-r--r--Objects/object.c58
3 files changed, 107 insertions, 45 deletions
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index dbab4cd..342b993 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -231,45 +231,38 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value)
*
* First, common helpers
*/
-static const char *
-get_name(PyObject *func) {
- assert(PyObject_TypeCheck(func, &PyMethodDescr_Type));
- return ((PyMethodDescrObject *)func)->d_method->ml_name;
-}
-
-typedef void (*funcptr)(void);
-
static inline int
method_check_args(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
assert(!PyErr_Occurred());
- assert(PyObject_TypeCheck(func, &PyMethodDescr_Type));
if (nargs < 1) {
- PyErr_Format(PyExc_TypeError,
- "descriptor '%.200s' of '%.100s' "
- "object needs an argument",
- get_name(func), PyDescr_TYPE(func)->tp_name);
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ if (funcstr != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "unbound method %U needs an argument", funcstr);
+ Py_DECREF(funcstr);
+ }
return -1;
}
PyObject *self = args[0];
- if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
- (PyObject *)PyDescr_TYPE(func)))
- {
- PyErr_Format(PyExc_TypeError,
- "descriptor '%.200s' for '%.100s' objects "
- "doesn't apply to a '%.100s' object",
- get_name(func), PyDescr_TYPE(func)->tp_name,
- Py_TYPE(self)->tp_name);
+ PyObject *dummy;
+ if (descr_check((PyDescrObject *)func, self, &dummy)) {
return -1;
}
if (kwnames && PyTuple_GET_SIZE(kwnames)) {
- PyErr_Format(PyExc_TypeError,
- "%.200s() takes no keyword arguments", get_name(func));
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ if (funcstr != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%U takes no keyword arguments", funcstr);
+ Py_DECREF(funcstr);
+ }
return -1;
}
return 0;
}
+typedef void (*funcptr)(void);
+
static inline funcptr
method_enter_call(PyThreadState *tstate, PyObject *func)
{
@@ -387,8 +380,12 @@ method_vectorcall_NOARGS(
return NULL;
}
if (nargs != 1) {
- PyErr_Format(PyExc_TypeError,
- "%.200s() takes no arguments (%zd given)", get_name(func), nargs-1);
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ if (funcstr != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%U takes no arguments (%zd given)", funcstr, nargs-1);
+ Py_DECREF(funcstr);
+ }
return NULL;
}
PyCFunction meth = (PyCFunction)method_enter_call(tstate, func);
@@ -410,9 +407,13 @@ method_vectorcall_O(
return NULL;
}
if (nargs != 2) {
- PyErr_Format(PyExc_TypeError,
- "%.200s() takes exactly one argument (%zd given)",
- get_name(func), nargs-1);
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ if (funcstr != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%U takes exactly one argument (%zd given)",
+ funcstr, nargs-1);
+ Py_DECREF(funcstr);
+ }
return NULL;
}
PyCFunction meth = (PyCFunction)method_enter_call(tstate, func);
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index c780904..8f752c6 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -334,15 +334,6 @@ _PyCFunction_Fini(void)
*
* First, common helpers
*/
-static const char *
-get_name(PyObject *func)
-{
- assert(PyCFunction_Check(func));
- PyMethodDef *method = ((PyCFunctionObject *)func)->m_ml;
- return method->ml_name;
-}
-
-typedef void (*funcptr)(void);
static inline int
cfunction_check_kwargs(PyThreadState *tstate, PyObject *func, PyObject *kwnames)
@@ -350,13 +341,19 @@ cfunction_check_kwargs(PyThreadState *tstate, PyObject *func, PyObject *kwnames)
assert(!_PyErr_Occurred(tstate));
assert(PyCFunction_Check(func));
if (kwnames && PyTuple_GET_SIZE(kwnames)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "%.200s() takes no keyword arguments", get_name(func));
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ if (funcstr != NULL) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%U takes no keyword arguments", funcstr);
+ Py_DECREF(funcstr);
+ }
return -1;
}
return 0;
}
+typedef void (*funcptr)(void);
+
static inline funcptr
cfunction_enter_call(PyThreadState *tstate, PyObject *func)
{
@@ -412,9 +409,12 @@ cfunction_vectorcall_NOARGS(
}
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
if (nargs != 0) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "%.200s() takes no arguments (%zd given)",
- get_name(func), nargs);
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ if (funcstr != NULL) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%U takes no arguments (%zd given)", funcstr, nargs);
+ Py_DECREF(funcstr);
+ }
return NULL;
}
PyCFunction meth = (PyCFunction)cfunction_enter_call(tstate, func);
@@ -436,9 +436,12 @@ cfunction_vectorcall_O(
}
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
if (nargs != 1) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "%.200s() takes exactly one argument (%zd given)",
- get_name(func), nargs);
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ if (funcstr != NULL) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "%U takes exactly one argument (%zd given)", funcstr, nargs);
+ Py_DECREF(funcstr);
+ }
return NULL;
}
PyCFunction meth = (PyCFunction)cfunction_enter_call(tstate, func);
diff --git a/Objects/object.c b/Objects/object.c
index 9536d46..3e61282 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -681,6 +681,64 @@ PyObject_Bytes(PyObject *v)
return PyBytes_FromObject(v);
}
+
+/*
+def _PyObject_FunctionStr(x):
+ try:
+ qualname = x.__qualname__
+ except AttributeError:
+ return str(x)
+ try:
+ mod = x.__module__
+ if mod is not None and mod != 'builtins':
+ return f"{x.__module__}.{qualname}()"
+ except AttributeError:
+ pass
+ return qualname
+*/
+PyObject *
+_PyObject_FunctionStr(PyObject *x)
+{
+ _Py_IDENTIFIER(__module__);
+ _Py_IDENTIFIER(__qualname__);
+ _Py_IDENTIFIER(builtins);
+ assert(!PyErr_Occurred());
+ PyObject *qualname;
+ int ret = _PyObject_LookupAttrId(x, &PyId___qualname__, &qualname);
+ if (qualname == NULL) {
+ if (ret < 0) {
+ return NULL;
+ }
+ return PyObject_Str(x);
+ }
+ PyObject *module;
+ PyObject *result = NULL;
+ ret = _PyObject_LookupAttrId(x, &PyId___module__, &module);
+ if (module != NULL && module != Py_None) {
+ PyObject *builtinsname = _PyUnicode_FromId(&PyId_builtins);
+ if (builtinsname == NULL) {
+ goto done;
+ }
+ ret = PyObject_RichCompareBool(module, builtinsname, Py_NE);
+ if (ret < 0) {
+ // error
+ goto done;
+ }
+ if (ret > 0) {
+ result = PyUnicode_FromFormat("%S.%S()", module, qualname);
+ goto done;
+ }
+ }
+ else if (ret < 0) {
+ goto done;
+ }
+ result = PyUnicode_FromFormat("%S()", qualname);
+done:
+ Py_DECREF(qualname);
+ Py_XDECREF(module);
+ return result;
+}
+
/* For Python 3.0.1 and later, the old three-way comparison has been
completely removed in favour of rich comparisons. PyObject_Compare() and
PyObject_Cmp() are gone, and the builtin cmp function no longer exists.