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
This commit is contained in:
Barry Warsaw 2017-10-05 12:11:18 -04:00 committed by GitHub
parent 4d07189788
commit 36c1d1f1e5
9 changed files with 324 additions and 23 deletions

View file

@ -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",