diff options
author | Barry Warsaw <barry@python.org> | 2017-10-05 16:11:18 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-05 16:11:18 (GMT) |
commit | 36c1d1f1e52ba54007cbecb42c5599e5ff62aa52 (patch) | |
tree | 317c296450800bfb25851dd92a62050e26c4c4fd /Python/sysmodule.c | |
parent | 4d071897880b7e84e1a217ebe19971c118316970 (diff) | |
download | cpython-36c1d1f1e52ba54007cbecb42c5599e5ff62aa52.zip cpython-36c1d1f1e52ba54007cbecb42c5599e5ff62aa52.tar.gz cpython-36c1d1f1e52ba54007cbecb42c5599e5ff62aa52.tar.bz2 |
PEP 553 built-in breakpoint() function (bpo-31353) (#3355)
Implement PEP 553, built-in breakpoint() with support from sys.breakpointhook(), along with documentation and tests. Closes bpo-31353
Diffstat (limited to 'Python/sysmodule.c')
-rw-r--r-- | Python/sysmodule.c | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 6d2cc96..e38a200 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -96,6 +96,81 @@ PySys_SetObject(const char *name, PyObject *v) return PyDict_SetItemString(sd, name, v); } +static PyObject * +sys_breakpointhook(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *keywords) +{ + assert(!PyErr_Occurred()); + char *envar = Py_GETENV("PYTHONBREAKPOINT"); + + if (envar == NULL || strlen(envar) == 0) { + envar = "pdb.set_trace"; + } + else if (!strcmp(envar, "0")) { + /* The breakpoint is explicitly no-op'd. */ + Py_RETURN_NONE; + } + char *last_dot = strrchr(envar, '.'); + char *attrname = NULL; + PyObject *modulepath = NULL; + + if (last_dot == NULL) { + /* The breakpoint is a built-in, e.g. PYTHONBREAKPOINT=int */ + modulepath = PyUnicode_FromString("builtins"); + attrname = envar; + } + else { + /* Split on the last dot; */ + modulepath = PyUnicode_FromStringAndSize(envar, last_dot - envar); + attrname = last_dot + 1; + } + if (modulepath == NULL) { + return NULL; + } + + PyObject *fromlist = Py_BuildValue("(s)", attrname); + if (fromlist == NULL) { + Py_DECREF(modulepath); + return NULL; + } + PyObject *module = PyImport_ImportModuleLevelObject( + modulepath, NULL, NULL, fromlist, 0); + Py_DECREF(modulepath); + Py_DECREF(fromlist); + + if (module == NULL) { + goto error; + } + + PyObject *hook = PyObject_GetAttrString(module, attrname); + Py_DECREF(module); + + if (hook == NULL) { + goto error; + } + PyObject *retval = _PyObject_FastCallKeywords(hook, args, nargs, keywords); + Py_DECREF(hook); + return retval; + + error: + /* If any of the imports went wrong, then warn and ignore. */ + PyErr_Clear(); + int status = PyErr_WarnFormat( + PyExc_RuntimeWarning, 0, + "Ignoring unimportable $PYTHONBREAKPOINT: \"%s\"", envar); + if (status < 0) { + /* Printing the warning raised an exception. */ + return NULL; + } + /* The warning was (probably) issued. */ + Py_RETURN_NONE; +} + +PyDoc_STRVAR(breakpointhook_doc, +"breakpointhook(*args, **kws)\n" +"\n" +"This hook function is called by built-in breakpoint().\n" +); + /* Write repr(o) to sys.stdout using sys.stdout.encoding and 'backslashreplace' error handler. If sys.stdout has a buffer attribute, use sys.stdout.buffer.write(encoded), otherwise redecode the string and use @@ -1365,6 +1440,8 @@ sys_getandroidapilevel(PyObject *self) static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ + {"breakpointhook", (PyCFunction)sys_breakpointhook, + METH_FASTCALL | METH_KEYWORDS, breakpointhook_doc}, {"callstats", (PyCFunction)sys_callstats, METH_NOARGS, callstats_doc}, {"_clear_type_cache", sys_clear_type_cache, METH_NOARGS, @@ -1977,6 +2054,9 @@ _PySys_BeginInit(void) PyDict_GetItemString(sysdict, "displayhook")); SET_SYS_FROM_STRING_BORROW("__excepthook__", PyDict_GetItemString(sysdict, "excepthook")); + SET_SYS_FROM_STRING_BORROW( + "__breakpointhook__", + PyDict_GetItemString(sysdict, "breakpointhook")); SET_SYS_FROM_STRING("version", PyUnicode_FromString(Py_GetVersion())); SET_SYS_FROM_STRING("hexversion", |