Last active
November 21, 2022 15:21
-
-
Save csghone/e095e69415ebfb420bff52f631718d4b to your computer and use it in GitHub Desktop.
Python "breakpoint()" with VSCode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import sys | |
import debugpy | |
# Based on | |
# https://stackoverflow.com/questions/69690653/remote-debugging-with-debugpy-works-from-code-but-not-from-command-line | |
# To use with 'breakpoint()' | |
# export PYTHONBREAKPOINT=vscode_debugger.wait_for_vscode | |
""" | |
VS Code configuration | |
Add the following to the "configurations" in your launch.json. | |
You can get the port number by running (To avoid clashes with multiple users): | |
python -c 'import os; print((20000 + os.getuid()) % 65536)' | |
{ | |
"name": "Python: Attach to process", | |
"type": "python", | |
"request": "attach", | |
"connect": { | |
"host": "localhost", | |
"port": 21001 // Refer comment above or 5678 for Windows | |
} | |
} | |
Using this in code: | |
To use with `breakpoint()` set `PYTHONBREAKPOINT=vscode_debugger.wait_for_vscode` before execution. | |
Alternatively, | |
`from vscode_debugger import *` | |
Add 'wait_for_vscode()' just before the line you want a breakpoint. | |
Alternately, add @vscode_debug decorator to any function you want a breakpoint. | |
Once the debugger connects, execution slows down. | |
You can speed it up a bit by adding 'disable_debugpy()' at the point | |
where you don't need a debugger. Breakpoints/stepping won't work after the | |
'disable_debugpy()' line | |
""" | |
LISTEN_ACTIVE = False | |
DEBUG_PORT = 5678 | |
if hasattr(os, getuid): | |
DEBUG_PORT = (30000 + os.getuid()) % 65536 # Edit if on Windows | |
def disable_debugpy(): | |
debugpy.server.api.get_global_debugger().disable_tracing() | |
print("Disabling debugpy trace", file=sys.stderr) | |
def wait_for_vscode(cond=True, breakpoint=True): | |
global LISTEN_ACTIVE | |
if not LISTEN_ACTIVE: | |
debugpy.listen(("localhost", DEBUG_PORT)) | |
LISTEN_ACTIVE = True | |
if cond and not debugpy.is_client_connected(): | |
print("Wait for DebugPy connection...", file=sys.stderr) | |
debugpy.wait_for_client() | |
if cond: | |
debugpy.breakpoint() | |
def vscode_debug_exception(func, *args, **kwargs): | |
def inner(): | |
try: | |
return func(*args, **kwargs) | |
except Exception as e: | |
import traceback; traceback.print_exc() | |
print(f"Exception: {type(e)}: {e} occured", | |
f"Will exit function: '{func.__name__}' and resume from there", | |
file=sys.stderr) | |
wait_for_vscode() | |
return inner | |
def vscode_debug(func, *args, **kwargs): | |
def inner(): | |
wait_for_vscode() | |
ret_val = func(*args, **kwargs) | |
disable_debugpy() | |
return ret_val | |
return inner | |
@vscode_debug_exception | |
def example_exception(): | |
print("Raising NameError Exception") | |
raise NameError | |
print("This will not execute!!") | |
@vscode_debug | |
def example_function(): | |
print("Debugpy waits at start of function") | |
def example_breakpoint1(): | |
try: | |
raise NameError | |
except: | |
print("Wait for DebugPy connection") | |
wait_for_vscode() | |
print("Debugger will not execute this line before connection") | |
def example_breakpoint2(): | |
try: | |
raise NameError | |
except: | |
print("Wait for DebugPy connection") | |
breakpoint() # Will work if export PYTHONBREAKPOINT=vscode_debugger.wait_for_vscode | |
print("This will execute after debugger is connected") | |
def check_execution_speed(): | |
import time | |
start_time = time.time() | |
_ = [x for x in range(1, 100000000)] | |
runtime = time.time() - start_time | |
print(f"Runtime: {runtime} seconds", flush=True) | |
def main(): | |
for i in range(1, 10): | |
wait_for_vscode(cond=(i==5)) | |
print(i) | |
example_exception() | |
example_function() | |
example_breakpoint1() | |
example_breakpoint2() | |
# When debugger is connected, execution speed drops. | |
# disable_debugpy() improves the speed, but not fully back to original levels. | |
check_execution_speed() | |
disable_debugpy() # Stepping/Breakpoints cannot work after this point. | |
check_execution_speed() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment