An improbable web debugger through WebSockets

Related tags

Debugging Tools wdb
Overview

wdb - Web Debugger

Build Status Coverage Status

Description

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

The wdb server which is responsible of managing debugging instances along with browser connections (through websockets) is based on Tornado. The wdb clients allow step by step debugging, in-program python code execution, code edition (based on CodeMirror) setting breakpoints...

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

wdb works with python 2 (2.6, 2.7), python 3 (3.2, 3.3, 3.4, 3.5) and pypy. Even better, it is possible to debug a python 2 program with a wdb server running on python 3 and vice-versa or debug a program running on a computer with a debugging server running on another computer inside a web page on a third computer!

Even betterer, it is now 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:

Global installation:

    $ pip install wdb.server

In virtualenv or with a different python installation:

    $ pip install wdb

(You must have the server installed and running)

Quick test

To try wdb, first you have to start the wdb server:

    $ wdb.server.py &

Optionally, you can automatically activate daemons with systemd (socket activation):

    $ cd /etc/systemd/system
    # curl -O https://raw.githubusercontent.com/Kozea/wdb/master/server/wdb.server.service
    # curl -O https://raw.githubusercontent.com/Kozea/wdb/master/server/wdb.server.socket
    # systemctl enable wdb.server.socket
    # systemctl start wdb.server.socket

Next run:

    $ python -m wdb your_file.py

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

You can access to 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 wdb
    wdb.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 wdb 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. (It's better to put the stop_trace in a finally block to avoid tracing all your program after an exception.)

Debugging web servers

wdb provides some tools to make it work nicely with different webservers:

Wsgi servers

For wsgi servers you can use the WdbMiddleware:

    from wdb.ext import WdbMiddleware
    wsgi_app = Whathever_wsgi_server_lib()
    my_app = WdbMiddleware(wsgi_app)
    my_app.serve_forever()
Flask

or using Flask:

    from flask import Flask
    from wdb.ext import WdbMiddleware
    app = Flask(__name__)
    app.debug = True
    app.wsgi_app = WdbMiddleware(app.wsgi_app)
    app.run(use_debugger=False) # Disable builtin Werkzeug debugger

you can also use the Flask-Wdb extension

    from flask import Flask
    from flask_wdb import Wdb

    app = Flask(__name__)
    app.debug = True
    Wdb(app)

    app.run()
Django

or using django:

Add the middleware in your wsgi.py:

After:

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

Add this:

    from wdb.ext import WdbMiddleware
    application = WdbMiddleware(application)

And in your settings.py, activate exception propagation:

    DEBUG = True
    DEBUG_PROPAGATE_EXCEPTIONS = True
CherryPy

or using CherryPy:

import cherrypy
from wdb.ext import WdbMiddleware

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', WdbMiddleware))

cherrypy.quickstart(app)

Tornado

In tornado, which is not a wsgi server, you can use the wdb_tornado function which will monkey patch the execute method on RequestHandlers:

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

Page loading time become slow

If wdb slows down too much of your application (tracing all the things takes time), you can start it disabled with:

    my_app = WdbMiddleware(wsgi_app, start_disabled=True)  # or
    wdb_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 wdb:

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

Start wdb server on computer A and launch this:

    WDB_NO_BROWSER_AUTO_OPEN=True python -m wdb p.py

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

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

    WDB_NO_BROWSER_AUTO_OPEN=True WDB_SOCKET_SERVER=computerC.addr WDB_SOCKET_PORT=19840 python -m wdb p.py

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

You can use different configurations:

See wdb.server.py --help for changing ports on server and these environnement vars for wdb instances:

WDB_SOCKET_SERVER         # WDB server host
WDB_SOCKET_PORT           # WDB server socket port
WDB_WEB_SERVER            # WDB server host for browser openning
WDB_WEB_PORT              # WDB server http port
WDB_NO_BROWSER_AUTO_OPEN  # To disable the automagic browser openning (which can't be done if the browser is not on the same machine)

Docker

If you are developing locally with Docker, you can also use wdb to debug a code running inside a container. The basic setup looks like this:

  1. Start wdb.server.py running in a container and expose port 1984 to your host computer, this will server the debugging web server.
  2. Start debugging in your app container, making sure to set WDB_SOCKET_SERVER to the address of the server container, and point it to the expoed port 19840 on that server.
  3. When a trace is reached, open up http://<your-docker-hostname>:1984

I will walk through this process in detail, using Docker Compose to set up the containers.

Let's say your docker-compose.yml looks like their example for using with Django:

db:
  image: postgres
web:
  build: .
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - .:/code
  ports:
    - "8000:8000"
  links:
    - db

Next lets add the wdb server part now and tell the web to link to it:

db:
  image: postgres
web:
  build: .
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - .:/code
  ports:
    - "8000:8000"
  links:
    - db
    - wdb
  environment:
    WDB_SOCKET_SERVER: wdb
    WDB_NO_BROWSER_AUTO_OPEN: True
wdb:
  image: kozea/wdb
  ports:
    - "1984:1984"

And add wdb to your requirements.txt in your web app:

$ echo 'wdb' >> requirements.txt

Now we can use wdb.set_trace() in our python app.

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

Then you have to 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 all you want in the prompt under the code. Multi-lines are partially supported using [Shift] + [Enter]. There is now help available by clicking on the top help button.

As of now the following special commands are supported during 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 follow 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 in page, useful to eval code in the source
*
* * arg is using the following 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 (ie: i == 10)
*   - [file]#function           : Break when inside `function` function
*
* File is always current file by default and 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.

Wdb Server

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

From there you should also see all python process and their threads running and you can try to pause them during their 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 wdb is installed for the python version running the program too.

Importing wdb each time is exhausting

Yes to avoid that, you can add a w builtin at the beggining of your application:

    from wdb.ext import add_w_builtin
    add_w_builtin()

you can now use the w object any where in your code:

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

Code completion

Wdb has dynamic code completion in 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 WDB 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

All contributions are more than welcomed, use the fork luke !

Or you still can use your money =)

Flattr gittip

Test

Wdb is now partially tested, so if you want to contribute be sure to run the test suite:

    $ pip install pytest
    $ pip install -e client -e server
    $ cd test
    $ py.test

Feel free to add tests !

Author

Florian Mounier @ Kozea

Licence

This library is licensed under GPLv3

wdb - An improbable web debugger through WebSockets

wdb Copyright (c) 2012-2016  Florian Mounier, Kozea

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
Comments
  • TypeError: the JSON object must be str, bytes or bytearray, not NoneType

    TypeError: the JSON object must be str, bytes or bytearray, not NoneType

    Hey,

    I am having trouble to start wdb. I have wdb and wdb.server installed in my virtualenv.

    I am placing import wdb; wdb.set_trace() inside my code, trying to start wdb server by running wdb.server.py & and trying to run my code with placed debug point.

    $ wdb.server.py &
    [4] 51913
    Traceback (most recent call last):
      File "/Users/rokas/.virtualenvs/virtualenv/bin/wdb.server.py", line 10, in <module>
        from wdb_server import server
      File "/Users/rokas/.virtualenvs/virtualenv/lib/python3.7/site-packages/wdb_server/__init__.py", line 374, in <module>
        raise_error=False
    TypeError: fetch() got multiple values for argument 'raise_error'
    [4]   Exit 1                  wdb.server.py
    
    $ python --version
    Python 3.7.2
    $ pip show wdb
    Name: wdb
    Version: 3.2.5
    $ pip show wdb.server
    Name: wdb.server
    Version: 3.2.5
    
    opened by roksys 10
  • 3.2.6 Traceback

    3.2.6 Traceback

    ...
    odoo_1           |   File "/opt/odoo/vendor/odoo/cc/odoo/models.py", line 415, in _build_model
    odoo_1           |     import wdb; wdb.set_trace()  # breakpoint 0c8862f7 //
    odoo_1           |   File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 1086, in set_trace
    odoo_1           |     wdb = Wdb.get(server=server, port=port)
    odoo_1           |   File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 118, in get
    odoo_1           |     Wdb.__init__(wdb, server, port, force_uuid)
    odoo_1           |   File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 169, in __init__
    odoo_1           |     self.get_breakpoints()
    odoo_1           |   File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 268, in get_breakpoints
    odoo_1           |     breaks = loads(breaks or '{}')
    odoo_1           |   File "/usr/lib/python3.5/json/__init__.py", line 319, in loads
    odoo_1           |     return _default_decoder.decode(s)
    odoo_1           |   File "/usr/lib/python3.5/json/decoder.py", line 339, in decode
    odoo_1           |     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    odoo_1           |   File "/usr/lib/python3.5/json/decoder.py", line 357, in raw_decode
    odoo_1           |     raise JSONDecodeError("Expecting value", s, err.value) from None
    odoo_1           | json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
    

    the value offending breaks == 'Quit'; True

    opened by blaggacao 8
  • Docker container client cannot connect with Server container

    Docker container client cannot connect with Server container

    I'm trying to use this tool for debugging my project.

    I installed the client in the main container and i'm using two different images with the server inside. Each time i try to debug my code i get this:

    Error:
    Odoo Server Error
    
    Traceback (most recent call last):
      File "/usr/lib/python3/dist-packages/odoo/fields.py", line 937, in __get__
        value = record.env.cache.get(record, self)
      File "/usr/lib/python3/dist-packages/odoo/api.py", line 960, in get
        value = self._data[field][record.id][key]
    KeyError: <odoo.api.Environment object at 0x7f72d88aeda0>
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/usr/lib/python3/dist-packages/odoo/http.py", line 650, in _handle_exception
        return super(JsonRequest, self)._handle_exception(exception)
      File "/usr/lib/python3/dist-packages/odoo/http.py", line 310, in _handle_exception
        raise pycompat.reraise(type(exception), exception, sys.exc_info()[2])
      File "/usr/lib/python3/dist-packages/odoo/tools/pycompat.py", line 87, in reraise
        raise value
      File "/usr/lib/python3/dist-packages/odoo/http.py", line 692, in dispatch
        result = self._call_function(**self.params)
      File "/usr/lib/python3/dist-packages/odoo/http.py", line 342, in _call_function
        return checked_call(self.db, *args, **kwargs)
      File "/usr/lib/python3/dist-packages/odoo/service/model.py", line 97, in wrapper
        return f(dbname, *args, **kwargs)
      File "/usr/lib/python3/dist-packages/odoo/http.py", line 335, in checked_call
        result = self.endpoint(*a, **kw)
      File "/usr/lib/python3/dist-packages/odoo/http.py", line 936, in __call__
        return self.method(*args, **kw)
      File "/usr/lib/python3/dist-packages/odoo/http.py", line 515, in response_wrap
        response = f(*args, **kw)
      File "/usr/lib/python3/dist-packages/odoo/addons/web/controllers/main.py", line 934, in call_kw
        return self._call_kw(model, method, args, kwargs)
      File "/usr/lib/python3/dist-packages/odoo/addons/web/controllers/main.py", line 926, in _call_kw
        return call_kw(request.env[model], method, args, kwargs)
      File "/usr/lib/python3/dist-packages/odoo/api.py", line 689, in call_kw
        return call_kw_multi(method, model, args, kwargs)
      File "/usr/lib/python3/dist-packages/odoo/api.py", line 680, in call_kw_multi
        result = method(recs, *args, **kwargs)
      File "/usr/lib/python3/dist-packages/odoo/models.py", line 2601, in read
        values[name] = field.convert_to_read(record[name], record, use_name_get)
      File "/usr/lib/python3/dist-packages/odoo/models.py", line 4759, in __getitem__
        return self._fields[key].__get__(self, type(self))
      File "/usr/lib/python3/dist-packages/odoo/fields.py", line 941, in __get__
        self.determine_value(record)
      File "/usr/lib/python3/dist-packages/odoo/fields.py", line 1052, in determine_value
        self.compute_value(recs)
      File "/usr/lib/python3/dist-packages/odoo/fields.py", line 1008, in compute_value
        self._compute_value(records)
      File "/usr/lib/python3/dist-packages/odoo/fields.py", line 999, in _compute_value
        getattr(records, self.compute)()
      File "/var/lib/odoo/addons/11.0/project/sale_order_project/models/project.py", line 13, in _compute_sale_orders_count
        wdb.set_trace()
      File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 994, in set_trace
        wdb = Wdb.get(server=server, port=port)
      File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 103, in get
        Wdb.__init__(wdb, server, port, force_uuid)
      File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 150, in __init__
        self.get_breakpoints()
      File "/usr/local/lib/python3.5/dist-packages/wdb/__init__.py", line 243, in get_breakpoints
        breaks = loads(breaks)
      File "/usr/lib/python3.5/json/__init__.py", line 312, in loads
        s.__class__.__name__))
    TypeError: the JSON object must be str, not 'NoneType'
    

    I am using compose for orquestation:

        web:
            restart: always
            build: odoo
            image: odoo
            networks:
                - odoo-network
            container_name: odoo.web
            ports:
                - '8069:8069'
                - '8072:8072'
            environment:
                - PASSWORD
                - WDB_NO_BROWSER_AUTO_OPEN
                - WDB_SOCKET_SERVER
                - WERKZEUG_DEBUG_PIN
            depends_on:
                - db
                - debug
                - mailhog
            volumes:
                - 'odoo-web-data:/var/lib/odoo'
                - './addons:/var/lib/odoo/addons/11.0'
                - './odoo/odoo.conf:/etc/odoo/odoo.conf'
        db:
            restart: always
            image: postgres
            networks:
                - odoo-network
            container_name: odoo.db
            environment:
                - POSTGRES_DB
                - POSTGRES_PASSWORD
                - POSTGRES_USER
        debug:
            # image: yajo/wdb-server
            image: fabricadesoftwarelibre/libregob-depurador:18.1
            networks:
                - odoo-network
            container_name: odoo.debug
            ports:
                - "1984:1984"
    

    Main server can connect with the database without any problem.

    I'm using this image for server: https://hub.docker.com/r/yajo/wdb-server/ And i installed the cliente here:

    FROM debian:stretch
    ...
    RUN pip3 install --upgrade wheel
    RUN pip3 install --upgrade bokeh \
            boto3 \
            email_validator \
            fdfgen \
            imageio \
            moviepy \
            num2words \
            numpy \
            phonenumbers \
            Pygments \
            psycopg2 \
            Wand \
            # watchdog \
            wdb \
            xlrd \
            xlsxwriter \
            xlwt
    

    Why cannot the cliente connect with the server?

    Thank you.

    opened by SalahAdDin 7
  • Syntax Error when establishing a WebSocket

    Syntax Error when establishing a WebSocket

    Hey dude, this piece of code should be

         function Websocket(wdb, uuid) {
           var proto;
           this.wdb = wdb;
           Websocket.__super__.constructor.apply(this, arguments);
           proto = document.location.protocol === "https:" ? "wss:" : "ws:";
    +++    this.url = `${proto}//` + document.location.host + "/websocket/" + uuid;
    ---    this.url = "{proto}//" + document.location.host + "/websocket/" + uuid;
           this.log('Opening new socket', this.url);
           this.ws = new WebSocket(this.url);
           this.ws.onclose = this.close.bind(this);
           this.ws.onopen = this.open.bind(this);
           this.ws.onerror = this.error.bind(this);
           this.ws.onmessage = this.message.bind(this);
         }
    
    

    https://github.com/Kozea/wdb/blob/master/server/wdb_server/static/javascripts/wdb.js#L73

    Also, the pypi package is obsolete. No static/hipster.jpg. web.server.py --unminified is broken because wdb.js is not bundled in.

    opened by Congee 7
  • Sluggish to get variables values

    Sluggish to get variables values

    I'm experiencing very sluggish variable evaluation, as you can see in this screencast.

    However, stepping in and out works quick as always.

    Any clues on why this is happening? I'm on wdb 3.1.2 (both client and server).

    opened by yajo 7
  • Handle uncommon python path installations

    Handle uncommon python path installations

    With this change, the first two libpython* detection routines can probably be removed, though, in that case, only processes running off the custom path will be handled.

    opened by s0undt3ch 7
  • connection lost at receive:855

    connection lost at receive:855

    Tried to try wdb, and did roughly what is described in readme, thus:

    1. installed virtualenv
    2. pip installed wdb and wdb.server
    3. ran wdb.server.py in one terminal
    4. created file ee.py with following content:
    import wdb
    wdb.set_trace()
    print("foo")
    print("jbar")
    
    
    
    1. tried to debug it, in another terminal:
    (venv) lukasz.mach@mac ~/tmp
     $ python ee.py
    2019-08-16 15:18:38,289 wdb Connection lost at receive:855
    Traceback (most recent call last):
      File "ee.py", line 2, in <module>
        wdb.set_trace()
      File "/Users/lukasz.mach/tmp/venv/lib/python3.5/site-packages/wdb/__init__.py", line 1086, in set_trace
        wdb = Wdb.get(server=server, port=port)
      File "/Users/lukasz.mach/tmp/venv/lib/python3.5/site-packages/wdb/__init__.py", line 118, in get
        Wdb.__init__(wdb, server, port, force_uuid)
      File "/Users/lukasz.mach/tmp/venv/lib/python3.5/site-packages/wdb/__init__.py", line 169, in __init__
        self.get_breakpoints()
      File "/Users/lukasz.mach/tmp/venv/lib/python3.5/site-packages/wdb/__init__.py", line 268, in get_breakpoints
        breaks = loads(breaks or '{}')
      File "/Users/lukasz.mach/.pyenv/versions/3.5.6/lib/python3.5/json/__init__.py", line 319, in loads
        return _default_decoder.decode(s)
      File "/Users/lukasz.mach/.pyenv/versions/3.5.6/lib/python3.5/json/decoder.py", line 339, in decode
        obj, end = self.raw_decode(s, idx=_w(s, 0).end())
      File "/Users/lukasz.mach/.pyenv/versions/3.5.6/lib/python3.5/json/decoder.py", line 357, in raw_decode
        raise JSONDecodeError("Expecting value", s, err.value) from None
    json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
    (venv) lukasz.mach@mac ~/tmp
    
    

    Any idea what I do wrong?

    System: Macos Mojave, pyenv, python 3.5.6 (same effect in python 3.7.x)

    opened by maho 6
  • Automated Docker builds

    Automated Docker builds

    I've seen that there is a separate repository for Docker builds (https://github.com/Kozea/docker-wdb-server) which is not updated regularly.

    I created an automated build from my fork (https://github.com/akalipetis/wdb), configuring it to tag releases correctly, which should work as expected, though I believe that it would be best if it was under the correct namespace (kozea/wdb).

    I have opened a new PR, which should make Docker builds reproducible and allow different versions to be built correctly, even at a later point in time.

    The only thing needed is for someone having access to both this repository and the Docker Hub team to create a rule that:

    • Builds master as latest
    • Builds all tags as the name of the tag

    There is extensive documentation about Docker automated builds here, but I'd be happy to help in any way I can.

    opened by akalipetis 6
  • Postmortem Issue

    Postmortem Issue

    Hi, I get the following traceback:

    2016-08-06 18:11:25,259 6 ERROR feat_test_sales_incl openerp.http: Exception during JSON request handling.
    Traceback (most recent call last):
      File "/home/src/odoo-cc/openerp/http.py", line 646, in _handle_exception
        return super(JsonRequest, self)._handle_exception(exception)
      File "/home/src/odoo-cc/openerp/http.py", line 286, in _handle_exception
        openerp.tools.config, sys.exc_info())
      File "/home/src/odoo-cc/openerp/tools/debugger.py", line 17, in post_mortem
        pdb.post_mortem(info[2])
      File "/usr/local/lib/python2.7/dist-packages/wdb/__init__.py", line 971, in post_mortem
        wdb.interaction(None, t)
      File "/usr/local/lib/python2.7/dist-packages/wdb/__init__.py", line 780, in interaction
        timeout=timeout)
      File "/usr/local/lib/python2.7/dist-packages/wdb/ui.py", line 88, in __init__
        self.stack, self.trace, self.index = self.db.get_trace(frame, tb)
      File "/usr/local/lib/python2.7/dist-packages/wdb/__init__.py", line 660, in get_trace
        if filename[0] == '<' and filename[-1] == '>':
    IndexError: string index out of range
    
    opened by blaggacao 6
  • Add official docker image

    Add official docker image

    opened by yajo 6
  • Not working

    Not working

    I've been unable to get the test (python -m wdb.test) or my own test app to work. Accessing either http://localhost:1984 or ``http://localhost:1984/wtf` will show "Wdb is tracing your request. It may take some time......" and continue to append periods, but never proceeds (I've waiting up to 10 minutes). Here's a screenshot - http://imgur.com/qQg9zwa

    opened by techniq 6
  • Dictionary key repr not escaped.

    Dictionary key repr not escaped.

    Seems related to the very old #81 in that certain values (dictionary keys, in our case) are not escaped.

    Use of repr() as keys results in apparently no keys. (Web inspector shows that they're being interpreted as HTML.)

    MCVE

    Enter the following in a presented interactive REPL or traceback:

    {'<a href="about:blank">Testing</a>': 'value'}
    

    Or, attempt to use the REPR of a class as keys:

    {repr(type): 'value'}
    

    This was exceptionally confusing when our plugin registry showed an empty string for every key.

    Screenshot

    Screenshot 2022-12-06 at 10 36 38
    opened by amcgregor 0
  • Bump qs from 6.8.0 to 6.11.0 in /server

    Bump qs from 6.8.0 to 6.11.0 in /server

    Bumps qs from 6.8.0 to 6.11.0.

    Changelog

    Sourced from qs's changelog.

    6.11.0

    • [New] [Fix] stringify: revert 0e903c0; add commaRoundTrip option (#442)
    • [readme] fix version badge

    6.10.5

    • [Fix] stringify: with arrayFormat: comma, properly include an explicit [] on a single-item array (#434)

    6.10.4

    • [Fix] stringify: with arrayFormat: comma, include an explicit [] on a single-item array (#441)
    • [meta] use npmignore to autogenerate an npmignore file
    • [Dev Deps] update eslint, @ljharb/eslint-config, aud, has-symbol, object-inspect, tape

    6.10.3

    • [Fix] parse: ignore __proto__ keys (#428)
    • [Robustness] stringify: avoid relying on a global undefined (#427)
    • [actions] reuse common workflows
    • [Dev Deps] update eslint, @ljharb/eslint-config, object-inspect, tape

    6.10.2

    • [Fix] stringify: actually fix cyclic references (#426)
    • [Fix] stringify: avoid encoding arrayformat comma when encodeValuesOnly = true (#424)
    • [readme] remove travis badge; add github actions/codecov badges; update URLs
    • [Docs] add note and links for coercing primitive values (#408)
    • [actions] update codecov uploader
    • [actions] update workflows
    • [Tests] clean up stringify tests slightly
    • [Dev Deps] update eslint, @ljharb/eslint-config, aud, object-inspect, safe-publish-latest, tape

    6.10.1

    • [Fix] stringify: avoid exception on repeated object values (#402)

    6.10.0

    • [New] stringify: throw on cycles, instead of an infinite loop (#395, #394, #393)
    • [New] parse: add allowSparse option for collapsing arrays with missing indices (#312)
    • [meta] fix README.md (#399)
    • [meta] only run npm run dist in publish, not install
    • [Dev Deps] update eslint, @ljharb/eslint-config, aud, has-symbols, tape
    • [Tests] fix tests on node v0.6
    • [Tests] use ljharb/actions/node/install instead of ljharb/actions/node/run
    • [Tests] Revert "[meta] ignore eclint transitive audit warning"

    6.9.7

    • [Fix] parse: ignore __proto__ keys (#428)
    • [Fix] stringify: avoid encoding arrayformat comma when encodeValuesOnly = true (#424)
    • [Robustness] stringify: avoid relying on a global undefined (#427)
    • [readme] remove travis badge; add github actions/codecov badges; update URLs
    • [Docs] add note and links for coercing primitive values (#408)
    • [Tests] clean up stringify tests slightly
    • [meta] fix README.md (#399)
    • Revert "[meta] ignore eclint transitive audit warning"

    ... (truncated)

    Commits
    • 56763c1 v6.11.0
    • ddd3e29 [readme] fix version badge
    • c313472 [New] [Fix] stringify: revert 0e903c0; add commaRoundTrip option
    • 95bc018 v6.10.5
    • 0e903c0 [Fix] stringify: with arrayFormat: comma, properly include an explicit `[...
    • ba9703c v6.10.4
    • 4e44019 [Fix] stringify: with arrayFormat: comma, include an explicit [] on a s...
    • 113b990 [Dev Deps] update object-inspect
    • c77f38f [Dev Deps] update eslint, @ljharb/eslint-config, aud, has-symbol, tape
    • 2cf45b2 [meta] use npmignore to autogenerate an npmignore file
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • support tornado 6.x or wdb won't run on python 3.10+

    support tornado 6.x or wdb won't run on python 3.10+

    Currently wdb-server 3.3.0 requires tornado<6.0,>=5.0. The newest version of tornado is v6.1.

    In Python 3.10 MutableMapping was moved

    • from collections.MutableMapping
    • to collections.abc.MutableMapping and this causes the following error when starting WDB (which uses tornado 5.1.1)
    (py3.10.4) root@a321832d390a:/# wdb.server.py 
    Traceback (most recent call last):
      File "/tcwa/venv/py3.10.4/bin/wdb.server.py", line 9, in <module>
        from tornado_systemd import SYSTEMD_SOCKET_FD, SystemdHTTPServer
      File "/tcwa/venv/py3.10.4/lib/python3.10/site-packages/tornado_systemd/__init__.py", line 4, in <module>
        from tornado.httpserver import HTTPServer
      File "/tcwa/venv/py3.10.4/lib/python3.10/site-packages/tornado/httpserver.py", line 33, in <module>
        from tornado.http1connection import HTTP1ServerConnection, HTTP1ConnectionParameters
      File "/tcwa/venv/py3.10.4/lib/python3.10/site-packages/tornado/http1connection.py", line 30, in <module>
        from tornado import httputil
      File "/tcwa/venv/py3.10.4/lib/python3.10/site-packages/tornado/httputil.py", line 107, in <module>
        class HTTPHeaders(collections.MutableMapping):
    AttributeError: module 'collections' has no attribute 'MutableMapping'
    

    Of course you can't patch tornado v5.x since it's not the newest major version and it was correctly developed before python 3.10 came out.

    So wdb should use Tornado 6.1+

    opened by massimiliano-della-rovere 0
  • Bump grunt from 1.0.4 to 1.5.3 in /server

    Bump grunt from 1.0.4 to 1.5.3 in /server

    Bumps grunt from 1.0.4 to 1.5.3.

    Release notes

    Sourced from grunt's releases.

    v1.5.3

    • Merge pull request #1745 from gruntjs/fix-copy-op 572d79b
    • Patch up race condition in symlink copying. 58016ff
    • Merge pull request #1746 from JamieSlome/patch-1 0749e1d
    • Create SECURITY.md 69b7c50

    https://github.com/gruntjs/grunt/compare/v1.5.2...v1.5.3

    v1.5.2

    • Update Changelog 7f15fd5
    • Merge pull request #1743 from gruntjs/cleanup-link b0ec6e1
    • Clean up link handling 433f91b

    https://github.com/gruntjs/grunt/compare/v1.5.1...v1.5.2

    v1.5.1

    • Merge pull request #1742 from gruntjs/update-symlink-test ad22608
    • Fix symlink test 0652305

    https://github.com/gruntjs/grunt/compare/v1.5.0...v1.5.1

    v1.5.0

    • Updated changelog b2b2c2b
    • Merge pull request #1740 from gruntjs/update-deps-22-10 3eda6ae
    • Update testing matrix 47d32de
    • More updates 2e9161c
    • Remove console log 04b960e
    • Update dependencies, tests... aad3d45
    • Merge pull request #1736 from justlep/main fdc7056
    • support .cjs extension e35fe54

    https://github.com/gruntjs/grunt/compare/v1.4.1...v1.5.0

    v1.4.1

    • Update Changelog e7625e5
    • Merge pull request #1731 from gruntjs/update-options 5d67e34
    • Fix ci install d13bf88
    • Switch to Actions 08896ae
    • Update grunt-known-options eee0673
    • Add note about a breaking change 1b6e288

    https://github.com/gruntjs/grunt/compare/v1.4.0...v1.4.1

    v1.4.0

    • Merge pull request #1728 from gruntjs/update-deps-changelog 63b2e89
    • Update changelog and util dep 106ed17
    • Merge pull request #1727 from gruntjs/update-deps-apr 49de70b
    • Update CLI and nodeunit 47cf8b6
    • Merge pull request #1722 from gruntjs/update-through e86db1c
    • Update deps 4952368

    ... (truncated)

    Changelog

    Sourced from grunt's changelog.

    v1.5.3 date: 2022-04-23 changes: - Patch up race condition in symlink copying. v1.5.2 date: 2022-04-12 changes: - Unlink symlinks when copy destination is a symlink. v1.5.1 date: 2022-04-11 changes: - Fixed symlink destination handling. v1.5.0 date: 2022-04-10 changes: - Updated dependencies. - Add symlink handling for copying files. v1.4.1 date: 2021-05-24 changes: - Fix --preload option to be a known option - Switch to GitHub Actions v1.4.0 date: 2021-04-21 changes: - Security fixes in production and dev dependencies - Liftup/Liftoff upgrade breaking change. Update your scripts to use --preload instead of --require. Ref: https://github.com/js-cli/js-liftoff/commit/e7a969d6706e730d90abb4e24d3cb4d3bce06ddb. v1.3.0 date: 2020-08-18 changes: - Switch to use safeLoad for loading YML files via file.readYAML. - Upgrade legacy-log to ~3.0.0. - Upgrade legacy-util to ~2.0.0. v1.2.1 date: 2020-07-07 changes: - Remove path-is-absolute dependency. (PR: gruntjs/grunt#1715) v1.2.0 date: 2020-07-03 changes: - Allow usage of grunt plugins that are located in any location that is visible to Node.js and NPM, instead of node_modules directly inside package that have a dev dependency to these plugins. (PR: gruntjs/grunt#1677) - Removed coffeescript from dependencies. To ease transition, if coffeescript is still around, Grunt will attempt to load it. If it is not, and the user loads a CoffeeScript file, Grunt will print a useful error indicating that the coffeescript package should be installed as a dev dependency.

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump path-parse from 1.0.6 to 1.0.7 in /server

    Bump path-parse from 1.0.6 to 1.0.7 in /server

    Bumps path-parse from 1.0.6 to 1.0.7.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump glob-parent from 5.0.0 to 5.1.2 in /server

    Bump glob-parent from 5.0.0 to 5.1.2 in /server

    Bumps glob-parent from 5.0.0 to 5.1.2.

    Release notes

    Sourced from glob-parent's releases.

    v5.1.2

    Bug Fixes

    v5.1.1

    Bug Fixes

    v5.1.0

    Features

    • add flipBackslashes option to disable auto conversion of slashes (closes #24) (#25) (eecf91d)
    Changelog

    Sourced from glob-parent's changelog.

    5.1.2 (2021-03-06)

    Bug Fixes

    6.0.0 (2021-05-03)

    ⚠ BREAKING CHANGES

    • Correct mishandled escaped path separators (#34)
    • upgrade scaffold, dropping node <10 support

    Bug Fixes

    • Correct mishandled escaped path separators (#34) (32f6d52), closes #32

    Miscellaneous Chores

    • upgrade scaffold, dropping node <10 support (e83d0c5)

    5.1.1 (2021-01-27)

    Bug Fixes

    5.1.0 (2021-01-27)

    Features

    • add flipBackslashes option to disable auto conversion of slashes (closes #24) (#25) (eecf91d)
    Commits
    • eb2c439 chore: update changelog
    • 12bcb6c chore: release 5.1.2
    • f923116 fix: eliminate ReDoS (#36)
    • 0b014a7 chore: add JSDoc returns information (#33)
    • 2b24ebd chore: generate initial changelog
    • 9b6e874 chore: release 5.1.1
    • 749c35e ci: try wrapping the JOB_ID in a string
    • 5d39def ci: attempt to switch to published coveralls
    • 0b5b37f ci: put the npm step back in for only Windows
    • 473f5d8 ci: update azure build images
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
Owner
Kozea
We build open source software that you will love.
Kozea
Full-screen console debugger for Python

PuDB: a console-based visual debugger for Python Its goal is to provide all the niceties of modern GUI-based debuggers in a more lightweight and keybo

Andreas Klöckner 2.6k Jan 1, 2023
Debugger capable of attaching to and injecting code into python processes.

DISCLAIMER: This is not an official google project, this is just something I wrote while at Google. Pyringe What this is Pyringe is a python debugger

Google 1.6k Dec 15, 2022
pdb++, a drop-in replacement for pdb (the Python debugger)

pdb++, a drop-in replacement for pdb What is it? This module is an extension of the pdb module of the standard library. It is meant to be fully compat

null 1k Jan 2, 2023
Graphical Python debugger which lets you easily view the values of all evaluated expressions

birdseye birdseye is a Python debugger which records the values of expressions in a function call and lets you easily view them after the function exi

Alex Hall 1.5k Dec 24, 2022
Voltron is an extensible debugger UI toolkit written in Python.

Voltron is an extensible debugger UI toolkit written in Python. It aims to improve the user experience of various debuggers (LLDB, GDB, VDB an

snare 5.9k Dec 30, 2022
PINCE is a front-end/reverse engineering tool for the GNU Project Debugger (GDB), focused on games.

PINCE is a front-end/reverse engineering tool for the GNU Project Debugger (GDB), focused on games. However, it can be used for any reverse-engi

Korcan Karaokçu 1.5k Jan 1, 2023
NoPdb: Non-interactive Python Debugger

NoPdb: Non-interactive Python Debugger Installation: pip install nopdb Docs: https://nopdb.readthedocs.io/ NoPdb is a programmatic (non-interactive) d

Ondřej Cífka 67 Oct 15, 2022
Tracing instruction in lldb debugger.Just a python-script for lldb.

lldb-trace Tracing instruction in lldb debugger. just a python-script for lldb. How to use it? Break at an address where you want to begin tracing. Im

null 156 Jan 1, 2023
Little helper to run Steam apps under Proton with a GDB debugger

protongdb A small little helper for running games with Proton and debugging with GDB Requirements At least Python 3.5 protontricks pip package and its

Joshie 21 Nov 27, 2022
Full featured multi arch/os debugger built on top of PyQt5 and frida

Full featured multi arch/os debugger built on top of PyQt5 and frida

iGio90 1.1k Dec 26, 2022
Arghonaut is an interactive interpreter, visualizer, and debugger for Argh! and Aargh!

Arghonaut Arghonaut is an interactive interpreter, visualizer, and debugger for Argh! and Aargh!, which are Befunge-like esoteric programming language

Aaron Friesen 2 Dec 10, 2021
A simple rubber duck debugger

Rubber Duck Debugger I found myself many times asking a question on StackOverflow or to one of my colleagues just for finding the solution simply by d

null 1 Nov 10, 2021
Visual Interaction with Code - A portable visual debugger for python

VIC Visual Interaction with Code A simple tool for debugging and interacting with running python code. This tool is designed to make it easy to inspec

Nathan Blank 1 Nov 16, 2021
Hdbg - Historical Debugger

hdbg - Historical Debugger This is in no way a finished product. Do not use this

Fivreld 2 Jan 2, 2022
Trashdbg - TrashDBG the world's worse debugger

The world's worse debugger Over the course of multiple OALABS Twitch streams we

OALabs 21 Jun 17, 2022
The official code of LM-Debugger, an interactive tool for inspection and intervention in transformer-based language models.

LM-Debugger is an open-source interactive tool for inspection and intervention in transformer-based language models. This repository includes the code

Mor Geva 110 Dec 28, 2022
A web-based visualization and debugging platform for NuPIC

Cerebro 2 A web-based visualization and debugging platform for NuPIC. Usage Set up cerebro2.server to export your model state. Then, run: cd static py

Numenta 24 Oct 13, 2021
An improbable web debugger through WebSockets

wdb - Web Debugger Description wdb is a full featured web debugger based on a client-server architecture. The wdb server which is responsible of manag

Kozea 1.6k Dec 9, 2022
A fast and durable Pub/Sub channel over Websockets. FastAPI + WebSockets + PubSub == ⚡ 💪 ❤️

⚡ ??️ FastAPI Websocket Pub/Sub A fast and durable Pub/Sub channel over Websockets. The easiest way to create a live publish / subscribe multi-cast ov

null 8 Dec 6, 2022
Burgeramt-appointments-websockets - Fetch Bürgeramt appointments and broadcast them via websockets

Bürgeramt appointment finder This server looks for Bürgeramt appointment every f

null 74 Dec 19, 2022