Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 553 – Built-in breakpoint()

Author:
Barry Warsaw <barry at python.org>
Status:
Final
Type:
Standards Track
Created:
05-Sep-2017
Python-Version:
3.7
Post-History:
05-Sep-2017, 07-Sep-2017, 13-Sep-2017
Resolution:
Python-Dev message

Table of Contents

Abstract

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.

Rationale

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.

  • It’s a lot to type (27 characters).
  • It’s easy to typo. The PEP author often mistypes this line, e.g. omittingthe semicolon, or typing a dot instead of an underscore.
  • It ties debugging directly to the choice of pdb. There might be otherdebugging options, say if you’re using an IDE or some other developmentenvironment.
  • Python linters (e.g. flake8[linters]) complain about this line because itcontains two statements. Breaking the idiom up into two lines complicatesits use because there are more opportunities for mistakes at clean up time.I.e. you might forget to delete one of those lines when you no longer needto debug the code.

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.

Proposal

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].

Environment variable

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:

  • Completely disabling all accidentalbreakpoint() 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.
  • IDE integration with specialized debuggers for embedded execution. The IDEwould run the program in its debugging environment withPYTHONBREAKPOINTset 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.

Implementation

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

Rejected alternatives

A new keyword

Originally, the author considered a new keyword, or an extension to anexisting keyword such asbreakhere. This is rejected on several fronts.

  • A brand new keyword would require a__future__ to enable it since almostany new keyword could conflict with existing code. This negates the easewith which you can enter the debugger.
  • An extended keyword such asbreakhere, 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.
  • A new keyword would require a modified grammar and likely a new bytecode.Each of these makes the implementation more complex. A new built-in breaksno existing code (since any existing module global would just shadow thebuilt-in) and is quite easy to implement.

sys.breakpoint()

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.

Version History

  • 2019-10-13
    • Add missingreturnNone inexcept clause to pseudo-code.
  • 2017-09-13
    • ThePYTHONBREAKPOINT environment variable is made a first classfeature.
  • 2017-09-07
    • debug() renamed tobreakpoint()
    • Signature changed tobreakpoint(*args,**kws) which is passed straightthrough tosys.breakpointhook().

References

[ipython-embed]
http://ipython.readthedocs.io/en/stable/api/generated/IPython.terminal.embed.html
[pdb-header]
https://docs.python.org/3.7/library/pdb.html#pdb.set_trace
[linters]
http://flake8.readthedocs.io/en/latest/
[js-debugger]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger
[hooks]
https://docs.python.org/3/library/sys.html#sys.displayhook
[syntax]
http://setuptools.readthedocs.io/en/latest/setuptools.html?highlight=console#automatic-script-creation
[impl]
https://github.com/python/cpython/pull/3355

Copyright

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


[8]ページ先頭

©2009-2025 Movatter.jp