mirror of
https://github.com/python/cpython.git
synced 2025-10-09 08:31:26 +00:00
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:
parent
4d07189788
commit
36c1d1f1e5
9 changed files with 324 additions and 23 deletions
|
@ -7,24 +7,24 @@ Built-in Functions
|
||||||
The Python interpreter has a number of functions and types built into it that
|
The Python interpreter has a number of functions and types built into it that
|
||||||
are always available. They are listed here in alphabetical order.
|
are always available. They are listed here in alphabetical order.
|
||||||
|
|
||||||
=================== ================= ================== ================ ====================
|
=================== ================= ================== ================== ====================
|
||||||
.. .. Built-in Functions .. ..
|
.. .. Built-in Functions .. ..
|
||||||
=================== ================= ================== ================ ====================
|
=================== ================= ================== ================== ====================
|
||||||
:func:`abs` |func-dict|_ :func:`help` :func:`min` :func:`setattr`
|
:func:`abs` :func:`delattr` :func:`hash` |func-memoryview|_ |func-set|_
|
||||||
:func:`all` :func:`dir` :func:`hex` :func:`next` :func:`slice`
|
:func:`all` |func-dict|_ :func:`help` :func:`min` :func:`setattr`
|
||||||
:func:`any` :func:`divmod` :func:`id` :func:`object` :func:`sorted`
|
:func:`any` :func:`dir` :func:`hex` :func:`next` :func:`slice`
|
||||||
:func:`ascii` :func:`enumerate` :func:`input` :func:`oct` :func:`staticmethod`
|
:func:`ascii` :func:`divmod` :func:`id` :func:`object` :func:`sorted`
|
||||||
:func:`bin` :func:`eval` :func:`int` :func:`open` |func-str|_
|
:func:`bin` :func:`enumerate` :func:`input` :func:`oct` :func:`staticmethod`
|
||||||
:func:`bool` :func:`exec` :func:`isinstance` :func:`ord` :func:`sum`
|
:func:`bool` :func:`eval` :func:`int` :func:`open` |func-str|_
|
||||||
|func-bytearray|_ :func:`filter` :func:`issubclass` :func:`pow` :func:`super`
|
:func:`breakpoint` :func:`exec` :func:`isinstance` :func:`ord` :func:`sum`
|
||||||
|func-bytes|_ :func:`float` :func:`iter` :func:`print` |func-tuple|_
|
|func-bytearray|_ :func:`filter` :func:`issubclass` :func:`pow` :func:`super`
|
||||||
:func:`callable` :func:`format` :func:`len` :func:`property` :func:`type`
|
|func-bytes|_ :func:`float` :func:`iter` :func:`print` |func-tuple|_
|
||||||
:func:`chr` |func-frozenset|_ |func-list|_ |func-range|_ :func:`vars`
|
:func:`callable` :func:`format` :func:`len` :func:`property` :func:`type`
|
||||||
:func:`classmethod` :func:`getattr` :func:`locals` :func:`repr` :func:`zip`
|
:func:`chr` |func-frozenset|_ |func-list|_ |func-range|_ :func:`vars`
|
||||||
:func:`compile` :func:`globals` :func:`map` :func:`reversed` :func:`__import__`
|
:func:`classmethod` :func:`getattr` :func:`locals` :func:`repr` :func:`zip`
|
||||||
|
:func:`compile` :func:`globals` :func:`map` :func:`reversed` :func:`__import__`
|
||||||
:func:`complex` :func:`hasattr` :func:`max` :func:`round`
|
:func:`complex` :func:`hasattr` :func:`max` :func:`round`
|
||||||
:func:`delattr` :func:`hash` |func-memoryview|_ |func-set|_
|
=================== ================= ================== ================== ====================
|
||||||
=================== ================= ================== ================ ====================
|
|
||||||
|
|
||||||
.. using :func:`dict` would create a link to another page, so local targets are
|
.. using :func:`dict` would create a link to another page, so local targets are
|
||||||
used, with replacement texts to make the output in the table consistent
|
used, with replacement texts to make the output in the table consistent
|
||||||
|
@ -113,6 +113,20 @@ are always available. They are listed here in alphabetical order.
|
||||||
.. index:: pair: Boolean; type
|
.. index:: pair: Boolean; type
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: breakpoint(*args, **kws)
|
||||||
|
|
||||||
|
This function drops you into the debugger at the call site. Specifically,
|
||||||
|
it calls :func:`sys.breakpointhook`, passing ``args`` and ``kws`` straight
|
||||||
|
through. By default, ``sys.breakpointhook()`` calls
|
||||||
|
:func:`pdb.set_trace()` expecting no arguments. In this case, it is
|
||||||
|
purely a convenience function so you don't have to explicitly import
|
||||||
|
:mod:`pdb` or type as much code to enter the debugger. However,
|
||||||
|
:func:`sys.breakpointhook` can be set to some other function and
|
||||||
|
:func:`breakpoint` will automatically call that, allowing you to drop into
|
||||||
|
the debugger of choice.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
.. _func-bytearray:
|
.. _func-bytearray:
|
||||||
.. class:: bytearray([source[, encoding[, errors]]])
|
.. class:: bytearray([source[, encoding[, errors]]])
|
||||||
:noindex:
|
:noindex:
|
||||||
|
|
|
@ -109,6 +109,40 @@ always available.
|
||||||
This function should be used for internal and specialized purposes only.
|
This function should be used for internal and specialized purposes only.
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: breakpointhook()
|
||||||
|
|
||||||
|
This hook function is called by built-in :func:`breakpoint`. By default,
|
||||||
|
it drops you into the :mod:`pdb` debugger, but it can be set to any other
|
||||||
|
function so that you can choose which debugger gets used.
|
||||||
|
|
||||||
|
The signature of this function is dependent on what it calls. For example,
|
||||||
|
the default binding (e.g. ``pdb.set_trace()``) expects no arguments, but
|
||||||
|
you might bind it to a function that expects additional arguments
|
||||||
|
(positional and/or keyword). The built-in ``breakpoint()`` function passes
|
||||||
|
its ``*args`` and ``**kws`` straight through. Whatever
|
||||||
|
``breakpointhooks()`` returns is returned from ``breakpoint()``.
|
||||||
|
|
||||||
|
The default implementation first consults the environment variable
|
||||||
|
:envvar:`PYTHONBREAKPOINT`. If that is set to ``"0"`` then this function
|
||||||
|
returns immediately; i.e. it is a no-op. If the environment variable is
|
||||||
|
not set, or is set to the empty string, ``pdb.set_trace()`` is called.
|
||||||
|
Otherwise this variable should name a function to run, using Python's
|
||||||
|
dotted-import nomenclature, e.g. ``package.subpackage.module.function``.
|
||||||
|
In this case, ``package.subpackage.module`` would be imported and the
|
||||||
|
resulting module must have a callable named ``function()``. This is run,
|
||||||
|
passing in ``*args`` and ``**kws``, and whatever ``function()`` returns,
|
||||||
|
``sys.breakpointhook()`` returns to the built-in :func:`breakpoint`
|
||||||
|
function.
|
||||||
|
|
||||||
|
Note that if anything goes wrong while importing the callable named by
|
||||||
|
:envvar:`PYTHONBREAKPOINT`, a :exc:`RuntimeWarning` is reported and the
|
||||||
|
breakpoint is ignored.
|
||||||
|
|
||||||
|
Also note that if ``sys.breakpointhook()`` is overridden programmatically,
|
||||||
|
:envvar:`PYTHONBREAKPOINT` is *not* consulted.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
.. function:: _debugmallocstats()
|
.. function:: _debugmallocstats()
|
||||||
|
|
||||||
Print low-level information to stderr about the state of CPython's memory
|
Print low-level information to stderr about the state of CPython's memory
|
||||||
|
@ -187,14 +221,19 @@ always available.
|
||||||
customized by assigning another three-argument function to ``sys.excepthook``.
|
customized by assigning another three-argument function to ``sys.excepthook``.
|
||||||
|
|
||||||
|
|
||||||
.. data:: __displayhook__
|
.. data:: __breakpointhook__
|
||||||
|
__displayhook__
|
||||||
__excepthook__
|
__excepthook__
|
||||||
|
|
||||||
These objects contain the original values of ``displayhook`` and ``excepthook``
|
These objects contain the original values of ``breakpointhook``,
|
||||||
at the start of the program. They are saved so that ``displayhook`` and
|
``displayhook``, and ``excepthook`` at the start of the program. They are
|
||||||
``excepthook`` can be restored in case they happen to get replaced with broken
|
saved so that ``breakpointhook``, ``displayhook`` and ``excepthook`` can be
|
||||||
|
restored in case they happen to get replaced with broken or alternative
|
||||||
objects.
|
objects.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
__breakpointhook__
|
||||||
|
|
||||||
|
|
||||||
.. function:: exc_info()
|
.. function:: exc_info()
|
||||||
|
|
||||||
|
|
|
@ -502,6 +502,18 @@ conflict.
|
||||||
:option:`-O` multiple times.
|
:option:`-O` multiple times.
|
||||||
|
|
||||||
|
|
||||||
|
.. envvar:: PYTHONBREAKPOINT
|
||||||
|
|
||||||
|
If this is set, it names a callable using dotted-path notation. The module
|
||||||
|
containing the callable will be imported and then the callable will be run
|
||||||
|
by the default implementation of :func:`sys.breakpointhook` which itself is
|
||||||
|
called by built-in :func:`breakpoint`. If not set, or set to the empty
|
||||||
|
string, it is equivalent to the value "pdb.set_trace". Setting this to the
|
||||||
|
string "0" causes the default implementation of :func:`sys.breakpointhook`
|
||||||
|
to do nothing but return immediately.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
.. envvar:: PYTHONDEBUG
|
.. envvar:: PYTHONDEBUG
|
||||||
|
|
||||||
If this is set to a non-empty string it is equivalent to specifying the
|
If this is set to a non-empty string it is equivalent to specifying the
|
||||||
|
|
|
@ -107,6 +107,25 @@ locale remains active when the core interpreter is initialized.
|
||||||
:pep:`538` -- Coercing the legacy C locale to a UTF-8 based locale
|
:pep:`538` -- Coercing the legacy C locale to a UTF-8 based locale
|
||||||
PEP written and implemented by Nick Coghlan.
|
PEP written and implemented by Nick Coghlan.
|
||||||
|
|
||||||
|
.. _whatsnew37-pep553:
|
||||||
|
|
||||||
|
PEP 553: Built-in breakpoint()
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
:pep:`553` describes a new built-in called ``breakpoint()`` which makes it
|
||||||
|
easy and consistent to enter the Python debugger. Built-in ``breakpoint()``
|
||||||
|
calls ``sys.breakpointhook()``. By default, this latter imports ``pdb`` and
|
||||||
|
then calls ``pdb.set_trace()``, but by binding ``sys.breakpointhook()`` to the
|
||||||
|
function of your choosing, ``breakpoint()`` can enter any debugger. Or, the
|
||||||
|
environment variable :envvar:`PYTHONBREAKPOINT` can be set to the callable of
|
||||||
|
your debugger of choice. Set ``PYTHONBREAKPOINT=0`` to completely disable
|
||||||
|
built-in ``breakpoint()``.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
:pep:`553` -- Built-in breakpoint()
|
||||||
|
PEP written and implemented by Barry Warsaw
|
||||||
|
|
||||||
|
|
||||||
Other Language Changes
|
Other Language Changes
|
||||||
======================
|
======================
|
||||||
|
|
|
@ -17,9 +17,12 @@ import traceback
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
import warnings
|
import warnings
|
||||||
|
from contextlib import ExitStack
|
||||||
from operator import neg
|
from operator import neg
|
||||||
from test.support import TESTFN, unlink, check_warnings
|
from test.support import (
|
||||||
|
EnvironmentVarGuard, TESTFN, check_warnings, swap_attr, unlink)
|
||||||
from test.support.script_helper import assert_python_ok
|
from test.support.script_helper import assert_python_ok
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
try:
|
try:
|
||||||
import pty, signal
|
import pty, signal
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -1514,6 +1517,111 @@ class BuiltinTest(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, tp, 1, 2)
|
self.assertRaises(TypeError, tp, 1, 2)
|
||||||
self.assertRaises(TypeError, tp, a=1, b=2)
|
self.assertRaises(TypeError, tp, a=1, b=2)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBreakpoint(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# These tests require a clean slate environment. For example, if the
|
||||||
|
# test suite is run with $PYTHONBREAKPOINT set to something else, it
|
||||||
|
# will mess up these tests. Similarly for sys.breakpointhook.
|
||||||
|
# Cleaning the slate here means you can't use breakpoint() to debug
|
||||||
|
# these tests, but I think that's okay. Just use pdb.set_trace() if
|
||||||
|
# you must.
|
||||||
|
self.resources = ExitStack()
|
||||||
|
self.addCleanup(self.resources.close)
|
||||||
|
self.env = self.resources.enter_context(EnvironmentVarGuard())
|
||||||
|
del self.env['PYTHONBREAKPOINT']
|
||||||
|
self.resources.enter_context(
|
||||||
|
swap_attr(sys, 'breakpointhook', sys.__breakpointhook__))
|
||||||
|
|
||||||
|
def test_breakpoint(self):
|
||||||
|
with patch('pdb.set_trace') as mock:
|
||||||
|
breakpoint()
|
||||||
|
mock.assert_called_once()
|
||||||
|
|
||||||
|
def test_breakpoint_with_breakpointhook_set(self):
|
||||||
|
my_breakpointhook = MagicMock()
|
||||||
|
sys.breakpointhook = my_breakpointhook
|
||||||
|
breakpoint()
|
||||||
|
my_breakpointhook.assert_called_once_with()
|
||||||
|
|
||||||
|
def test_breakpoint_with_breakpointhook_reset(self):
|
||||||
|
my_breakpointhook = MagicMock()
|
||||||
|
sys.breakpointhook = my_breakpointhook
|
||||||
|
breakpoint()
|
||||||
|
my_breakpointhook.assert_called_once_with()
|
||||||
|
# Reset the hook and it will not be called again.
|
||||||
|
sys.breakpointhook = sys.__breakpointhook__
|
||||||
|
with patch('pdb.set_trace') as mock:
|
||||||
|
breakpoint()
|
||||||
|
mock.assert_called_once_with()
|
||||||
|
my_breakpointhook.assert_called_once_with()
|
||||||
|
|
||||||
|
def test_breakpoint_with_args_and_keywords(self):
|
||||||
|
my_breakpointhook = MagicMock()
|
||||||
|
sys.breakpointhook = my_breakpointhook
|
||||||
|
breakpoint(1, 2, 3, four=4, five=5)
|
||||||
|
my_breakpointhook.assert_called_once_with(1, 2, 3, four=4, five=5)
|
||||||
|
|
||||||
|
def test_breakpoint_with_passthru_error(self):
|
||||||
|
def my_breakpointhook():
|
||||||
|
pass
|
||||||
|
sys.breakpointhook = my_breakpointhook
|
||||||
|
self.assertRaises(TypeError, breakpoint, 1, 2, 3, four=4, five=5)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.flags.ignore_environment, '-E was given')
|
||||||
|
def test_envar_good_path_builtin(self):
|
||||||
|
self.env['PYTHONBREAKPOINT'] = 'int'
|
||||||
|
with patch('builtins.int') as mock:
|
||||||
|
breakpoint('7')
|
||||||
|
mock.assert_called_once_with('7')
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.flags.ignore_environment, '-E was given')
|
||||||
|
def test_envar_good_path_other(self):
|
||||||
|
self.env['PYTHONBREAKPOINT'] = 'sys.exit'
|
||||||
|
with patch('sys.exit') as mock:
|
||||||
|
breakpoint()
|
||||||
|
mock.assert_called_once_with()
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.flags.ignore_environment, '-E was given')
|
||||||
|
def test_envar_good_path_noop_0(self):
|
||||||
|
self.env['PYTHONBREAKPOINT'] = '0'
|
||||||
|
with patch('pdb.set_trace') as mock:
|
||||||
|
breakpoint()
|
||||||
|
mock.assert_not_called()
|
||||||
|
|
||||||
|
def test_envar_good_path_empty_string(self):
|
||||||
|
# PYTHONBREAKPOINT='' is the same as it not being set.
|
||||||
|
self.env['PYTHONBREAKPOINT'] = ''
|
||||||
|
with patch('pdb.set_trace') as mock:
|
||||||
|
breakpoint()
|
||||||
|
mock.assert_called_once_with()
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.flags.ignore_environment, '-E was given')
|
||||||
|
def test_envar_unimportable(self):
|
||||||
|
for envar in (
|
||||||
|
'.', '..', '.foo', 'foo.', '.int', 'int.'
|
||||||
|
'nosuchbuiltin',
|
||||||
|
'nosuchmodule.nosuchcallable',
|
||||||
|
):
|
||||||
|
with self.subTest(envar=envar):
|
||||||
|
self.env['PYTHONBREAKPOINT'] = envar
|
||||||
|
mock = self.resources.enter_context(patch('pdb.set_trace'))
|
||||||
|
w = self.resources.enter_context(check_warnings(quiet=True))
|
||||||
|
breakpoint()
|
||||||
|
self.assertEqual(
|
||||||
|
str(w.message),
|
||||||
|
f'Ignoring unimportable $PYTHONBREAKPOINT: "{envar}"')
|
||||||
|
self.assertEqual(w.category, RuntimeWarning)
|
||||||
|
mock.assert_not_called()
|
||||||
|
|
||||||
|
def test_envar_ignored_when_hook_is_set(self):
|
||||||
|
self.env['PYTHONBREAKPOINT'] = 'sys.exit'
|
||||||
|
with patch('sys.exit') as mock:
|
||||||
|
sys.breakpointhook = int
|
||||||
|
breakpoint()
|
||||||
|
mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(pty, "the pty and signal modules must be available")
|
@unittest.skipUnless(pty, "the pty and signal modules must be available")
|
||||||
class PtyTests(unittest.TestCase):
|
class PtyTests(unittest.TestCase):
|
||||||
"""Tests that use a pseudo terminal to guarantee stdin and stdout are
|
"""Tests that use a pseudo terminal to guarantee stdin and stdout are
|
||||||
|
|
|
@ -3523,7 +3523,8 @@ class TestSignatureDefinitions(unittest.TestCase):
|
||||||
needs_semantic_update = {"round"}
|
needs_semantic_update = {"round"}
|
||||||
no_signature |= needs_semantic_update
|
no_signature |= needs_semantic_update
|
||||||
# These need *args support in Argument Clinic
|
# These need *args support in Argument Clinic
|
||||||
needs_varargs = {"min", "max", "print", "__build_class__"}
|
needs_varargs = {"breakpoint", "min", "max", "print",
|
||||||
|
"__build_class__"}
|
||||||
no_signature |= needs_varargs
|
no_signature |= needs_varargs
|
||||||
# These simply weren't covered in the initial AC conversion
|
# These simply weren't covered in the initial AC conversion
|
||||||
# for builtin callables
|
# for builtin callables
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
:pep:`553` - Add a new built-in called ``breakpoint()`` which calls
|
||||||
|
``sys.breakpointhook()``. By default this imports ``pdb`` and calls
|
||||||
|
``pdb.set_trace()``, but users may override ``sys.breakpointhook()`` to call
|
||||||
|
whatever debugger they want. The original value of the hook is saved in
|
||||||
|
``sys.__breakpointhook__``.
|
|
@ -422,6 +422,28 @@ builtin_callable(PyObject *module, PyObject *obj)
|
||||||
return PyBool_FromLong((long)PyCallable_Check(obj));
|
return PyBool_FromLong((long)PyCallable_Check(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
builtin_breakpoint(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *keywords)
|
||||||
|
{
|
||||||
|
PyObject *hook = PySys_GetObject("breakpointhook");
|
||||||
|
|
||||||
|
if (hook == NULL) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "lost sys.breakpointhook");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_INCREF(hook);
|
||||||
|
PyObject *retval = _PyObject_FastCallKeywords(hook, args, nargs, keywords);
|
||||||
|
Py_DECREF(hook);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(breakpoint_doc,
|
||||||
|
"breakpoint(*args, **kws)\n\
|
||||||
|
\n\
|
||||||
|
Call sys.breakpointhook(*args, **kws). sys.breakpointhook() must accept\n\
|
||||||
|
whatever arguments are passed.\n\
|
||||||
|
\n\
|
||||||
|
By default, this drops you into the pdb debugger.");
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
|
@ -2627,6 +2649,7 @@ static PyMethodDef builtin_methods[] = {
|
||||||
BUILTIN_ANY_METHODDEF
|
BUILTIN_ANY_METHODDEF
|
||||||
BUILTIN_ASCII_METHODDEF
|
BUILTIN_ASCII_METHODDEF
|
||||||
BUILTIN_BIN_METHODDEF
|
BUILTIN_BIN_METHODDEF
|
||||||
|
{"breakpoint", (PyCFunction)builtin_breakpoint, METH_FASTCALL | METH_KEYWORDS, breakpoint_doc},
|
||||||
BUILTIN_CALLABLE_METHODDEF
|
BUILTIN_CALLABLE_METHODDEF
|
||||||
BUILTIN_CHR_METHODDEF
|
BUILTIN_CHR_METHODDEF
|
||||||
BUILTIN_COMPILE_METHODDEF
|
BUILTIN_COMPILE_METHODDEF
|
||||||
|
|
|
@ -96,6 +96,81 @@ PySys_SetObject(const char *name, PyObject *v)
|
||||||
return PyDict_SetItemString(sd, name, 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'
|
/* Write repr(o) to sys.stdout using sys.stdout.encoding and 'backslashreplace'
|
||||||
error handler. If sys.stdout has a buffer attribute, use
|
error handler. If sys.stdout has a buffer attribute, use
|
||||||
sys.stdout.buffer.write(encoded), otherwise redecode the string and use
|
sys.stdout.buffer.write(encoded), otherwise redecode the string and use
|
||||||
|
@ -1365,6 +1440,8 @@ sys_getandroidapilevel(PyObject *self)
|
||||||
|
|
||||||
static PyMethodDef sys_methods[] = {
|
static PyMethodDef sys_methods[] = {
|
||||||
/* Might as well keep this in alphabetic order */
|
/* 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", (PyCFunction)sys_callstats, METH_NOARGS,
|
||||||
callstats_doc},
|
callstats_doc},
|
||||||
{"_clear_type_cache", sys_clear_type_cache, METH_NOARGS,
|
{"_clear_type_cache", sys_clear_type_cache, METH_NOARGS,
|
||||||
|
@ -1977,6 +2054,9 @@ _PySys_BeginInit(void)
|
||||||
PyDict_GetItemString(sysdict, "displayhook"));
|
PyDict_GetItemString(sysdict, "displayhook"));
|
||||||
SET_SYS_FROM_STRING_BORROW("__excepthook__",
|
SET_SYS_FROM_STRING_BORROW("__excepthook__",
|
||||||
PyDict_GetItemString(sysdict, "excepthook"));
|
PyDict_GetItemString(sysdict, "excepthook"));
|
||||||
|
SET_SYS_FROM_STRING_BORROW(
|
||||||
|
"__breakpointhook__",
|
||||||
|
PyDict_GetItemString(sysdict, "breakpointhook"));
|
||||||
SET_SYS_FROM_STRING("version",
|
SET_SYS_FROM_STRING("version",
|
||||||
PyUnicode_FromString(Py_GetVersion()));
|
PyUnicode_FromString(Py_GetVersion()));
|
||||||
SET_SYS_FROM_STRING("hexversion",
|
SET_SYS_FROM_STRING("hexversion",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue