diff --git a/Lib/test/test_sys_setprofile.py b/Lib/test/test_sys_setprofile.py index 16467e7f718..c2ecf8eeed9 100644 --- a/Lib/test/test_sys_setprofile.py +++ b/Lib/test/test_sys_setprofile.py @@ -350,6 +350,24 @@ class ProfileSimulatorTestCase(TestCaseBase): self.check_events(f, [(1, 'call', f_ident), (1, 'return', f_ident)]) + # Test an invalid call (bpo-34125) + def test_unbound_method_no_args(self): + kwargs = {} + def f(p): + dict.get(**kwargs) + f_ident = ident(f) + self.check_events(f, [(1, 'call', f_ident), + (1, 'return', f_ident)]) + + # Test an invalid call (bpo-34125) + def test_unbound_method_invalid_args(self): + kwargs = {} + def f(p): + dict.get(print, 42, **kwargs) + f_ident = ident(f) + self.check_events(f, [(1, 'call', f_ident), + (1, 'return', f_ident)]) + def ident(function): if hasattr(function, "f_code"): diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-07-23-16-34-03.bpo-34125.jCl2Q2.rst b/Misc/NEWS.d/next/Core and Builtins/2018-07-23-16-34-03.bpo-34125.jCl2Q2.rst new file mode 100644 index 00000000000..e6036b4d41e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-07-23-16-34-03.bpo-34125.jCl2Q2.rst @@ -0,0 +1 @@ +Profiling of unbound built-in methods now works when ``**kwargs`` is given. diff --git a/Python/ceval.c b/Python/ceval.c index 4a82bd3198a..d0f9915b4f0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4642,15 +4642,39 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) static PyObject * do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict) { + PyObject *result; + if (PyCFunction_Check(func)) { - PyObject *result; PyThreadState *tstate = PyThreadState_GET(); C_TRACE(result, PyCFunction_Call(func, callargs, kwdict)); return result; } - else { - return PyObject_Call(func, callargs, kwdict); + else if (Py_TYPE(func) == &PyMethodDescr_Type) { + PyThreadState *tstate = PyThreadState_GET(); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + if (nargs > 0 && tstate->use_tracing) { + /* We need to create a temporary bound method as argument + for profiling. + + If nargs == 0, then this cannot work because we have no + "self". In any case, the call itself would raise + TypeError (foo needs an argument), so we just skip + profiling. */ + PyObject *self = PyTuple_GET_ITEM(callargs, 0); + func = Py_TYPE(func)->tp_descr_get(func, self, (PyObject*)Py_TYPE(self)); + if (func == NULL) { + return NULL; + } + + C_TRACE(result, _PyCFunction_FastCallDict(func, + &PyTuple_GET_ITEM(callargs, 1), + nargs - 1, + kwdict)); + Py_DECREF(func); + return result; + } } + return PyObject_Call(func, callargs, kwdict); } /* Extract a slice index from a PyLong or an object with the