This PEP proposes adding a new built-in function calledbreakpoint() whichenters a Python debugger at the point of the call. Additionally, two newnames are added to thesys module to make the choice of which debugger isentered configurable.
Python has long had a great debugger in its standard library calledpdb.Setting a break point is commonly written like this:
foo()importpdb;pdb.set_trace()bar()
Thus after executingfoo() and before executingbar(), Python willenter the debugger. However this idiom has several disadvantages.
Python developers also have many other debuggers to choose from, butremembering how to invoke them can be problematic. For example, even whenIDEs have user interface for setting breakpoints, it may still be moreconvenient to just edit the code. The APIs for entering the debuggerprogrammatically are inconsistent, so it can be difficult to remember exactlywhat to type.
We can solve all these problems by providing a universal API for entering thedebugger, as proposed in this PEP.
The JavaScript language provides adebugger statement[js-debugger] which entersthe debugger at the point where the statement appears.
This PEP proposes a new built-in function calledbreakpoint()which enters a Python debugger at the call site. Thus the exampleabove would be written like so:
foo()breakpoint()bar()
Further, this PEP proposes two new name bindings for thesysmodule, calledsys.breakpointhook() andsys.__breakpointhook__. By default,sys.breakpointhook()implements the actual importing and entry intopdb.set_trace(),and it can be set to a different function to change the debugger thatbreakpoint() enters.
sys.__breakpointhook__ is initialized to the same function assys.breakpointhook() so that you can always easily resetsys.breakpointhook() to the default value (e.g. by doingsys.breakpointhook=sys.__breakpointhook__). This is exactly the same ashow the existingsys.displayhook() /sys.__displayhook__ andsys.excepthook() /sys.__excepthook__ work[hooks].
The signature of the built-in isbreakpoint(*args,**kws). The positionaland keyword arguments are passed straight through tosys.breakpointhook()and the signatures must match or aTypeError will be raised. The returnfromsys.breakpointhook() is passed back up to, and returned frombreakpoint().
The rationale for this is based on the observation that the underlyingdebuggers may accept additional optional arguments. For example, IPythonallows you to specify a string that gets printed when the break point isentered[ipython-embed]. As of Python 3.7, the pdb module also supports anoptionalheader argument[pdb-header].
The default implementation ofsys.breakpointhook() consults a newenvironment variable calledPYTHONBREAKPOINT. This environment variablecan have various values:
PYTHONBREAKPOINT=0 disables debugging. Specifically, with this valuesys.breakpointhook() returnsNone immediately.PYTHONBREAKPOINT= (i.e. the empty string). This is the same as notsetting the environment variable at all, in which casepdb.set_trace()is run as usual.PYTHONBREAKPOINT=some.importable.callable. In this case,sys.breakpointhook() imports thesome.importable module and gets thecallable object from the resulting module, which it then calls. Thevalue may be a string with no dots, in which case it names a built-incallable, e.g.PYTHONBREAKPOINT=int. (Guido has expressed thepreference for normal Python dotted-paths, not setuptools-style entry pointsyntax[syntax].)This environment variable allows external processes to control how breakpointsare handled. Some uses cases include:
breakpoint() calls pushed toproduction. This could be accomplished by settingPYTHONBREAKPOINT=0 inthe execution environment. Another suggestion by reviewers of the PEP wasto setPYTHONBREAKPOINT=sys.exit in this case.PYTHONBREAKPOINTset to their internal debugging hook.PYTHONBREAKPOINT is re-interpreted every timesys.breakpointhook() isreached. This allows processes to change its value during the execution of aprogram and havebreakpoint() respond to those changes. It is notconsidered a performance critical section since entering a debugger bydefinition stops execution. Thus, programs can do the following:
os.environ['PYTHONBREAKPOINT']='foo.bar.baz'breakpoint()# Imports foo.bar and calls foo.bar.baz()
Overridingsys.breakpointhook defeats the default consultation ofPYTHONBREAKPOINT. It is up to the overriding code to consultPYTHONBREAKPOINT if they want.
If access to thePYTHONBREAKPOINT callable fails in any way (e.g. theimport fails, or the resulting module does not contain the callable), aRuntimeWarning is issued, and no breakpoint function is called.
Note that as with all otherPYTHON* environment variables,PYTHONBREAKPOINT is ignored when the interpreter is started with-E. This means the default behavior will occur(i.e.pdb.set_trace() will run). There was some discussion aboutalternatively treatingPYTHONBREAKPOINT=0 when-E as ineffect, but the opinions were inconclusive, so it was decided thatthis wasn’t special enough for a special case.
A pull request exists with the proposed implementation[impl].
While the actual implementation is in C, the Python pseudo-code for thisfeature looks roughly like the following:
# In builtins.defbreakpoint(*args,**kws):importsysmissing=object()hook=getattr(sys,'breakpointhook',missing)ifhookismissing:raiseRuntimeError('lost sys.breakpointhook')returnhook(*args,**kws)# In sys.defbreakpointhook(*args,**kws):importimportlib,os,warningshookname=os.getenv('PYTHONBREAKPOINT')ifhooknameisNoneorlen(hookname)==0:hookname='pdb.set_trace'elifhookname=='0':returnNonemodname,dot,funcname=hookname.rpartition('.')ifdot=='':modname='builtins'try:module=importlib.import_module(modname)hook=getattr(module,funcname)except:warnings.warn('Ignoring unimportable $PYTHONBREAKPOINT:{}'.format(hookname),RuntimeWarning)returnNonereturnhook(*args,**kws)__breakpointhook__=breakpointhook
Originally, the author considered a new keyword, or an extension to anexisting keyword such asbreakhere. This is rejected on several fronts.
__future__ to enable it since almostany new keyword could conflict with existing code. This negates the easewith which you can enter the debugger.breakhere, while more readable and notrequiring a__future__ would tie the keyword extension to this newfeature, preventing more useful extensions such as those proposed inPEP 548.Why notsys.breakpoint()? Requiring an import to invoke the debugger isexplicitly rejected becausesys is not imported in every module. Thatjust requires more typing and would lead to:
importsys;sys.breakpoint()
which inherits several of the problems this PEP aims to solve.
returnNone inexcept clause to pseudo-code.PYTHONBREAKPOINT environment variable is made a first classfeature.debug() renamed tobreakpoint()breakpoint(*args,**kws) which is passed straightthrough tosys.breakpointhook().This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0553.rst
Last modified:2025-02-01 08:55:40 GMT