Skip to content

[IMP] Add suppor for Pydevd/Pycharm #542

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

david-banon-tecnativa
Copy link
Contributor

@david-banon-tecnativa david-banon-tecnativa commented Jun 16, 2025

Scaffolding support for debugging with pydevd/pycharm.
Depends on Tecnativa/doodba#665
How to test: clone/update a template with the option --vcs-ref=dck-imp-pydev_debugger
Example:
copier update --vcs-ref=dck-imp-pydev_debugger --trust -f
Change dockerfile ref to
ghcr.io/tecnativa/doodba:${ODOO_VERSION}-pr-665-test-onbuild
Rebuild the image, run git aggregate and then start with --pycharm

invoke img-build git-aggregate
invoke start --pycharm

Pycharm has an issue where stopping the remote debugger causes a constant stream of notifications and errors, only resolved by restarting pycharm. That is unrelated to this PR and has been reported to JetBrains for a month.

@ap-wtioit
Copy link
Contributor

Nice to see preparations for official support for this. Which version of PyCharm would you be targeting?

Our current code to generate the mappings which requires to first save the run config as project file and it requires first creating a /=/ fake mapping in the run config to create the xml that we can patch. (works less well in current versions of PyCharm as they are only saving those files to the file system on pycharm close)

if [[ -e .idea/runConfigurations/ || -e .run ]] ; then
    function get_mappings() {
            # mapping paths adds support for clicking the stacktrace, as well as mapping the source in debugger
            while read -r line; do
                directory=$(dirname "$line")
                addon_name=$(basename "$directory")
                echo "$addon_name => $directory" 1>&2
                echo "$INDENT<mapping local-root=\"\$PROJECT_DIR\$/$directory\" remote-root=\"/opt/odoo/auto/addons/$addon_name\" />"
            done < <(find odoo/custom/src -name '__manifest__.py' | grep -v '/odoo/')
            if [[ -e odoo/custom/src/odoo/odoo-bin ]] ; then
                # since PyCharm 2022.2 it is necessary to specify a locally existing script, so we need to map the odoo-bin to /usr/local/bin/odoo
                # to be able to run odoo correctly
                echo "$INDENT<mapping local-root=\"\$PROJECT_DIR\$/odoo/custom/src/odoo/odoo-bin\" remote-root=\"/usr/local/bin/odoo\" />"
            fi
            echo "$INDENT<mapping local-root=\"\$PROJECT_DIR\$/odoo/custom/src/odoo/addons\" remote-root=\"/opt/odoo/auto/addons\" />"
            echo "$INDENT<mapping local-root=\"\$PROJECT_DIR\$/odoo/custom/src\" remote-root=\"/opt/odoo/custom/src\" />"
            # mapping for restore_encrypted.yaml
            echo "$INDENT<mapping local-root=\"\$PROJECT_DIR\$/restore.d\" remote-root=\"/opt/odoo/restore\" />"
    }

    TMP_NEW_MAPPING_FILE=""
    while read -r FILE; do
        if [[ -e "$FILE" ]] ; then
            if [[ "$FILE" == *".mapping" || "$FILE" == *".new_mapping" || "$FILE" == *".new" ]] ; then
                # file is a fragment of an old run
                rm "$FILE"
                continue
            fi
            TMP_FILE="${FILE}.new"
            TMP_MAPPING_FILE="${FILE}.mapping"
            INDENT="          "
            BASE_NAME=$(basename "$0")
            if [[ "$0" != "" && "$0" != -* && "$BASE_NAME" == "local_dev.sh" ]] ; then
                # we are not running inside of source ./local_dev.sh
                set -e
                DIR_NAME=$(dirname "$0")
                cd "$DIR_NAME"
            fi
            # It is important that the file has already any mapping set before
            grep '<mapping' "$FILE" > "$TMP_MAPPING_FILE"
            if [[ ! $TMP_NEW_MAPPING_FILE ]]; then
                TMP_NEW_MAPPING_FILE="${FILE}.new_mapping"
                get_mappings >> "$TMP_NEW_MAPPING_FILE"
            fi

            if ! diff "$TMP_MAPPING_FILE" "$TMP_NEW_MAPPING_FILE"; then
                echo "Updating path mapping '$FILE'"
                grep '<mapping' -m 1 --before-context=1024 "$FILE" | grep -v '<mapping' > "$TMP_FILE"
                cat "$TMP_NEW_MAPPING_FILE" >> "$TMP_FILE"
                # this is not working on mac osx (--after-context) so we use a short script for getting the last few lines
                #cat $FILE | grep '<mapping' -m 1 --after-context=1024 | grep -v '<mapping' >> "$TMP_FILE"
                last_mapping_line=$(grep '<mapping' -n "$FILE" | tail -n 1 | awk -F: '{print $1}')
                total_lines=$(wc -l "$FILE" | awk '{print $1}')
                newline_correction=$((1 - $(tail -c 1 "$FILE" | wc -l))) # file might not end in a newline (then we need to add 1 to the lines at the end)
                lines_at_the_end=$((total_lines - last_mapping_line + newline_correction))
                tail -n ${lines_at_the_end} "$FILE" >> "$TMP_FILE"
                # switch contents of the xml file
                mv "$FILE" "$FILE.bak"
                mv "$TMP_FILE" "$FILE"
                rm "$FILE.bak"
            else
                echo "Unchanged path mapping '$FILE'"
            fi
            rm "$TMP_MAPPING_FILE"
        fi
    done < <(grep -Rl '<mapping' .idea/runConfigurations/ .run/ 2>/dev/null)
    if [[ $TMP_NEW_MAPPING_FILE && -e "$TMP_NEW_MAPPING_FILE" ]] ; then
        rm "$TMP_NEW_MAPPING_FILE"
    fi
fi

@CarlosRoca13 CarlosRoca13 force-pushed the dck-imp-pydev_debugger branch 2 times, most recently from 5cd2db9 to 80367fb Compare June 16, 2025 11:35
@CarlosRoca13
Copy link
Member

CarlosRoca13 commented Jun 16, 2025

@ap-wtioit It’s almost ready to generate it using task.py, but we have a problem when reading the variables—we can’t figure out what’s going on. I’ll show you the error in the following image:
image

Do you have any idea why this might be happening?

Here you have the traceback

stock.picking(21,) ⇨ WH/OUT/00008
Unable to display children:Error resolving variables Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_resolver.py", line 254, in resolve
    return dct[key]
KeyError: '0'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_comm.py", line 832, in do_it
    type_name, val_dict = pydevd_vars.resolve_compound_variable_fields(
  File "/usr/local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_vars.py", line 139, in resolve_compound_variable_fields
    var = getVariable(dbg, thread_id, frame_id, scope, attrs)
  File "/usr/local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_constants.py", line 532, in new_func
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_vars.py", line 122, in getVariable
    var = resolver.resolve(var, k)
  File "/usr/local/lib/python3.8/site-packages/_pydevd_bundle/pydevd_resolver.py", line 256, in resolve
    return getattr(dct, key)
AttributeError: 'dict' object has no attribute '0'

@ap-wtioit
Copy link
Contributor

ap-wtioit commented Jun 16, 2025

Do you have any idea why this might be happening?

To me it looks like you have enabled the deep inspection of python variables in the current breakpoint context. This does not work well with Odoo's ORM / Cache.

Update: can you have a look what value you are using for "Variables Loading Policy".

Screenshot from 2025-06-16 13-55-05

The other reason for such stack traces (not the exact one) when debugging with PyCharm i get, is when the transaction is rollbacked but the debugger has not yet exited and is still trying to load variables through the ORM.

@josep-tecnativa josep-tecnativa force-pushed the dck-imp-pydev_debugger branch from 80367fb to f31ad34 Compare June 16, 2025 11:55
@CarlosRoca13
Copy link
Member

Yes, I have the same as you
image

@ap-wtioit
Copy link
Contributor

Yes, I have the same as you

ok, it also could be that the pydevd you are using is not compatible with your pycharm. it looks like you are using the one installed in the doodba image. i'm usually running the one provided by pycharm (and using PyCharm 2023.3.7 (Professional Edition) since the git commit dialog change in 2025.1) :

import sys; print('Python %s on %s' % (sys.version, sys.platform))
/usr/local/bin/python3 /opt/.pycharm_helpers/pydev/pydevd.py --multiprocess --qt-support=auto --client 172.16.0.5 --port 41239 --file /usr/local/bin/odoo -u portal_oauth_provider --test-enable --workers=0 --stop-after-init 
doodba INFO: Waiting until postgres is listening at db...
doodba INFO: Linking all addons from /opt/odoo/custom/src/addons.yaml in /opt/odoo/auto/addons
doodba INFO: Generating /opt/odoo/auto/odoo.conf file. Overriding any existing...
doodba INFO: Merging found configuration files in /opt/odoo/auto/odoo.conf
no zipped/encrypted file found, making sure /var/lib/odoo/filestore/devel/ exists
/opt/odoo/custom/src/odoo/odoo/modules/module.py:11: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  import pkg_resources
doodba INFO: replacing listening ip %s by %s
doodba INFO: Executing /usr/local/bin/python3 /opt/.pycharm_helpers/pydev/pydevd.py --multiprocess --qt-support=auto --client 172.16.0.129 --port 41239 --file /usr/local/bin/odoo -u portal_oauth_provider --test-enable --workers=0 --stop-after-init
/opt/.pycharm_helpers/pydev/pydevd_plugins/__init__.py:2: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  __import__('pkg_resources').declare_namespace(__name__)
Connected to pydev debugger (build 233.15619.17)
2025-06-16 11:53:19,198 1 INFO ? odoo: Odoo version 17.0 
2025-06-16 11:53:19,198 1 INFO ? odoo: Using configuration file at /opt/odoo/auto/odoo.conf 
2025-06-16 11:53:19,198 1 INFO ? odoo: addons paths: ['/opt/odoo/custom/src/odoo/odoo/addons', '/var/lib/odoo/addons/17.0', '/opt/odoo/auto/addons'] 
2025-06-16 11:53:19,198 1 INFO ? odoo: database: accounts@db:5432 
2025-06-16 11:53:19,334 1 INFO ? odoo.addons.base.models.ir_actions_report: Will use the Wkhtmltopdf binary at /usr/local/bin/wkhtmltopdf 
2025-06-16 11:53:19,528 1 INFO ? odoo.service.server: HTTP service (werkzeug) running on f8abf8e3f89d:8069 
2025-06-16 11:53:19,562 1 INFO devel odoo.modules.loading: loading 1 modules... 
2025-06-16 11:53:19,569 1 INFO devel odoo.modules.loading: 1 modules loaded in 0.01s, 0 queries (+0 extra) 
2025-06-16 11:53:19,602 1 INFO devel odoo.modules.loading: updating modules list 
2025-06-16 11:53:19,608 1 INFO devel odoo.addons.base.models.ir_module: ALLOW access to module.update_list on [] to user __system__ #1 via n/a 
2025-06-16 11:53:20,728 1 INFO devel odoo.addons.base.models.ir_module: ALLOW access to module.button_upgrade on ['Portal OAuth Provider'] to user __system__ #1 via n/a 
2025-06-16 11:53:20,728 1 INFO devel odoo.addons.base.models.ir_module: ALLOW access to module.update_list on ['Portal OAuth Provider'] to user __system__ #1 via n/a 
2025-06-16 11:53:21,377 1 INFO devel odoo.addons.base.models.ir_module: ALLOW access to module.button_install on [] to user __system__ #1 via n/a 
2025-06-16 11:53:21,434 1 INFO devel odoo.modules.loading: loading 27 modules... 
2025-06-16 11:53:22,065 1 INFO devel odoo.modules.loading: Loading module portal_oauth_provider (27/27)

(running a proprietary module test because this was the last working debug config for me, i don't have one with stock.picking installed/available)

The following lines:

doodba INFO: replacing listening ip 172.16.0.5 by 172.16.0.129
doodba INFO: Executing ...

are produced by our code to fix the pycharm run / debug / profiling / coverage in our base image direxec to fix the listening ip to one that is working inside the container (available in the containers interfaces) and forwarded with socat (docker-whitelist) dynamically to the correct dynamic pycharm port on 172.16.0.5:41239 by another script that watches for those ports and connects them.

But the docker / pydevd settings are highly specific for the PyCharm Major version as JetBrains is always changing the docker integration (and often breaking it).

@josep-tecnativa josep-tecnativa force-pushed the dck-imp-pydev_debugger branch from f31ad34 to bf5c8be Compare June 16, 2025 13:17
@josep-tecnativa josep-tecnativa force-pushed the dck-imp-pydev_debugger branch from bf5c8be to 0cb2a55 Compare June 16, 2025 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants