Skip to main content

An improbable web debugger through WebSockets (server)

Project description

dbgr - Web Debugger

PyPI

PyPI - dbgr PyPI - dbgr-server Python Versions

GitHub

GitHub last commit License

About this project

dbgr is a full featured web debugger based on a client-server architecture, similar in spirit to pudb but running as a web application instead of a terminal UI. It lets you step through code, inspect locals/globals in a live tree view, set breakpoints, and evaluate Python expressions, all from your browser.

This project is a continuation of the original wdb client/server debugger by Florian Mounier (Kozea), and of wdb_server_aiohttp, Shepilov Vladislav's aiohttp-based rewrite of the original tornado server. Both of those projects had gone stale (last releases several years old, built on stacks that have since been deprecated), so dbgr picks up where they left off: the server was migrated from aiohttp to FastAPI, the frontend editor was upgraded from CodeMirror 5 to CodeMirror 6, and a pudb-style variables/watches panel was added next to the source and console panes.

This is an actively maintained fork meant to keep the project usable on current Python and browser versions. Contributions, bug reports, and ideas for where to take it next are welcome — see Contribute below.

What changed from the original wdb/wdb_server_aiohttp

  • aiohttp -> FastAPI / uvicorn server
  • CodeMirror 5 -> CodeMirror 6 editor
  • Added a pudb-style variables/watches panel (locals, globals, and expandable containers) next to the source and console panes
  • tornado -> aiohttp (inherited from wdb_server_aiohttp) -> FastAPI
  • Coffeescript -> Typescript
  • Bower -> npm/yarn
  • Grunt -> Webpack
  • yapf -> black
  • Support for themes on any screen size
  • Support for Safari
  • Works without internet access (the original project required Google Fonts from a CDN)
  • Renamed packages/imports: wdb -> dbgr, wdb.server -> dbgr-server

Description

dbgr is a full featured web debugger based on a client-server architecture.

The dbgr server, which is responsible for managing debugging instances along with browser connections (through websockets), is based on FastAPI. The dbgr client allows step by step debugging, in-program python code execution, code edition (based on CodeMirror), and setting breakpoints.

Due to this architecture, all of this is fully compatible with multithread and multiprocess programs.

dbgr works with Python 3. It is possible to debug a program running on one computer with a debugging server running on another computer, inside a web page on a third computer.

It is also possible to pause a currently running python process/thread using code injection from the web interface (this requires gdb and ptrace enabled).

In other words, it's a very enhanced version of pdb directly in your browser, with nice features.

Installation

Install the server (this pulls in the web interface and the debugging protocol server):

$ pip install dbgr-server

In each virtualenv/interpreter you want to debug, install the client:

$ pip install dbgr

(You must have the server installed and running somewhere reachable from the process you're debugging.)

Quick test

To try dbgr, first start the dbgr server:

$ dbgr.server.py &

Next run:

$ python -m dbgr your_file.py

Dbgr will open a debugging window right in your browser, paused at the beginning of your program.

You can access http://localhost:1984/ to have an overview of the server.

NB: You have to start the server only once. Multiple debugging sessions can be run simultaneously without problem.

This is not the only way to debug a program, see below.

Usage

Setting trace

To debug any program, with the server on, just add:

import dbgr
dbgr.set_trace()

anywhere in your code. Your program will stop at the set_trace line (just like pdb).

Tracing code

To inspect your code on exception, you can do the following:

from dbgr import trace
with trace():
    wrong_code()

Any exception during wrong_code will launch a debugging session.

You can also use the start_trace() and stop_trace() methods (put stop_trace in a finally block to avoid tracing the rest of your program after an exception).

Debugging web servers

dbgr provides some tools to make it work nicely with different web servers:

WSGI servers

For WSGI servers you can use the DbgrMiddleware:

from dbgr.ext import DbgrMiddleware
wsgi_app = Whatever_wsgi_server_lib()
my_app = DbgrMiddleware(wsgi_app)
my_app.serve_forever()
Flask
from flask import Flask
from dbgr.ext import DbgrMiddleware
app = Flask(__name__)
app.debug = True
app.wsgi_app = DbgrMiddleware(app.wsgi_app)
app.run(use_debugger=False)  # Disable builtin Werkzeug debugger
Django

Add the middleware in your wsgi.py, after:

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

add:

from dbgr.ext import DbgrMiddleware
application = DbgrMiddleware(application)

And in your settings.py, activate exception propagation:

DEBUG = True
DEBUG_PROPAGATE_EXCEPTIONS = True
CherryPy
import cherrypy
from dbgr.ext import DbgrMiddleware

class HelloWorld(object):
    @cherrypy.expose
    def index(self):
        undefined_method()  # This will fail
        return "Hello World!"

cherrypy.config.update({'global': {'request.throw_errors': True}})
app = cherrypy.Application(HelloWorld())
app.wsgiapp.pipeline.append(('debugger', DbgrMiddleware))

cherrypy.quickstart(app)

Tornado

In Tornado, which is not a WSGI server, you can use the dbgr_tornado function, which will monkey-patch the execute method on RequestHandlers:

from dbgr.ext import dbgr_tornado
from tornado.web import Application
my_app = Application([(r"/", MainHandler)])
if options.debug:
    dbgr_tornado(my_app)
my_app.listen(8888)

Page loading time becomes slow

If dbgr slows down your application too much (tracing everything takes time), you can start it disabled with:

my_app = DbgrMiddleware(wsgi_app, start_disabled=True)  # or
dbgr_tornado(my_app, start_disabled=True)

Then when you get an exception just click on the on/off button.

Remote debugging

You can easily do remote debugging with dbgr:

Let's say you want to run a program p.py on computer A and you want to debug it on computer B.

Start the dbgr server on computer A and launch this:

DBGR_NO_BROWSER_AUTO_OPEN=True python -m dbgr p.py

And open a browser on computer B at the url given by the dbgr log.

Now you can also run the dbgr server on a computer C and run on computer A:

DBGR_NO_BROWSER_AUTO_OPEN=True DBGR_SOCKET_SERVER=computerC.addr DBGR_SOCKET_PORT=19840 python -m dbgr p.py

And go with computer B to http://computerC/debug/session/[uuid in log], where you can step into p.py running on computer A.

You can use different configurations. See dbgr.server.py --help for changing ports on the server, and these environment variables for dbgr instances:

DBGR_SOCKET_SERVER         # dbgr server host
DBGR_SOCKET_PORT           # dbgr server socket port
DBGR_WEB_SERVER            # dbgr server host for browser opening
DBGR_WEB_PORT               # dbgr server http port
DBGR_NO_BROWSER_AUTO_OPEN  # Disable automatic browser opening (needed if the browser is not on the same machine)

Docker

If you are developing locally with Docker, you can also use dbgr to debug code running inside a container. A Dockerfile for the server is included in this repo; build it locally with:

$ docker build -t dbgr-server .

The basic setup looks like this:

  1. Start the dbgr-server container and expose port 1984 to your host computer; this serves the debugging web UI.
  2. Start debugging in your app container, making sure to set DBGR_SOCKET_SERVER to the address of the server container, and point it to the exposed port 19840 on that server.
  3. When a trace is reached, open up http://<your-docker-hostname>:1984.

Example docker-compose.yml, starting from the official example for using Docker with Django:

db:
  image: postgres
web:
  build: .
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - .:/code
  ports:
    - "8000:8000"
  links:
    - db
    - dbgr
  environment:
    DBGR_SOCKET_SERVER: dbgr
    DBGR_NO_BROWSER_AUTO_OPEN: True
dbgr:
  image: dbgr-server
  ports:
    - "1984:1984"

Add dbgr to your requirements.txt in your web app:

$ echo 'dbgr' >> requirements.txt

Now you can use dbgr.set_trace() in your python app:

# ... some code
import dbgr
dbgr.set_trace()

Then rebuild your web application and start everything up again:

$ docker-compose stop
$ docker-compose build web
$ docker-compose up

Now you can access http://<local docker server>:1984 to see the traces as they come up in your app.

In browser usage

Once you are in a breakpoint or in an exception, you can eval anything you want in the prompt under the code. Multi-lines are partially supported using [Shift] + [Enter]. There is help available by clicking the top help button.

As of now, the following special commands are supported during a breakpoint:

.s or [Ctrl] + [↓] or [F11]    : Step into
.n or [Ctrl] + [→] or [F10]    : Step over (Next)
.r or [Ctrl] + [↑] or [F9]     : Step out (Return)
.c or [Ctrl] + [←] or [F8]     : Continue
.u or [F7]                     : Until (Next over loops)
.j lineno                      : Jump to lineno (must be at bottom frame and in the same function)
.b arg                         : Set a session breakpoint, see below for what arg can be
.t arg                         : Set a temporary breakpoint, arg follows the same syntax as .b
.z arg                         : Delete existing breakpoint
.l                             : List active breakpoints
.f                             : Echo all typed commands in the current debugging session
.d expression                  : Dump the result of expression in a table
.w expression                  : Watch expression in current file (click on the name to remove)
.q                             : Quit
.h                             : Get some help
.e                             : Toggle file edition mode
.g                             : Clear prompt
.i [mime/type;]expression      : Display the result in an embed; mime type is auto-detected on Linux and defaults to "text/html" otherwise
iterable!sthg                  : If cutter is installed, executes cut(iterable).sthg
expr >! file                   : Write the result of expr in file
!< file                        : Eval the content of file
[Enter]                        : Eval the current selected text on the page, useful to eval code in the source

* arg follows this syntax:
    [file/module][:lineno][#function][,condition]
  which means:
    - [file]                    : Break if any line of `file` is executed
    - [file]:lineno             : Break on `file` at `lineno`
    - [file][:lineno],condition : Break on `file` at `lineno` if `condition` is True (e.g.: i == 10)
    - [file]#function           : Break when inside `function`
  File is always the current file by default; you can also specify a module
  like `logging.config`.

You can also eval a variable in the source by middle-clicking on it. You can add/remove a breakpoint by clicking on the line number.

NB: Hotkeys with arrows are purposely not triggered in the eval prompt, to avoid conflicts when typing.

Dbgr Server

To see which debugging sessions are currently open, open your browser at http://localhost:1984/. You can also close crashed sessions.

From there you should also see all Python processes and their threads running, and you can try to pause them during execution to do step by step debugging and current variable inspection. This is highly experimental and requires gdb and a kernel with ptrace enabled to inject python code into a running python process. If you get ptrace: Operation not permitted. you will have to enable it.

Depending on your system it might work with:

$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

Make sure that dbgr is installed for the python version running the program too.

Importing dbgr each time is exhausting

To avoid that, you can add a w builtin at the beginning of your application:

from dbgr.ext import add_w_builtin
add_w_builtin()

you can now use the w object anywhere in your code:

my_code()
w.tf  # Stop next line
doubtful_code()
my_code()
with w.trace():
    doubtful_code()

Code completion

dbgr has dynamic code completion in the eval prompt thanks to jedi.

FAQ

In Firefox opened debugging pages are not closed when done

It's a Firefox config flag; visit about:config and set dom.allow_scripts_to_close_windows to true.

The logs are spammed with 'parsing Python module'

If your logging configuration is set to display DEBUG logs, you may see a log for every imported file in your project any time dbgr is active, like so:

DEBUG 2017-07-16 13:15:03,772 index 49835 123145573191680 parsing Python module /project/.virtualenv/python-3.6.1/lib/python3.6/site-packages/package/file.py for indexing

To silence only this message, add a config for the importmagic module. For example:

LOGGING = {
    ...
    'loggers': {
        ...
        'importmagic.index': {
            'level': 'ERROR',
            'propagate': False,
        },
    },
}

Contribute

This project picks up an old, abandoned debugger and brings it back to a modern stack, but there's plenty left to do (see TODO.org): finishing the CodeMirror 6 migration polish, expanding the variables/watches panel, covering the frontend with unit tests, adding frontend/backend integration tests, and multithread/multiprocess/async debugging support are all open. All contributions are more than welcome — fork it and send a PR.

Author

License

This library is licensed under GPLv3

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dbgr_server-1.1.0.dev1.tar.gz (9.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dbgr_server-1.1.0.dev1-py3-none-any.whl (9.1 MB view details)

Uploaded Python 3

File details

Details for the file dbgr_server-1.1.0.dev1.tar.gz.

File metadata

  • Download URL: dbgr_server-1.1.0.dev1.tar.gz
  • Upload date:
  • Size: 9.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for dbgr_server-1.1.0.dev1.tar.gz
Algorithm Hash digest
SHA256 4f8d20f4feb5cc8cb57065756842c81be8fec6dc089bb07fa66403083a30d9a2
MD5 84087d55eba57b55a5e3909fd290b347
BLAKE2b-256 67426311f698aac6d0fd59222805322a7e586597da1591271de7440aff253a59

See more details on using hashes here.

Provenance

The following attestation bundles were made for dbgr_server-1.1.0.dev1.tar.gz:

Publisher: publish.yml on arkhan/dbgr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dbgr_server-1.1.0.dev1-py3-none-any.whl.

File metadata

File hashes

Hashes for dbgr_server-1.1.0.dev1-py3-none-any.whl
Algorithm Hash digest
SHA256 124d99a324597469e359644f8c7064deda6c22428ed85942e8d5f8d9c61cb616
MD5 a20fc63f2ae84196b729116c87ec10a9
BLAKE2b-256 563ea8c944a9ef432e9e54e0259efb0e445c4ef18842a7b9e9777e16c8e3e13d

See more details on using hashes here.

Provenance

The following attestation bundles were made for dbgr_server-1.1.0.dev1-py3-none-any.whl:

Publisher: publish.yml on arkhan/dbgr

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page