Skip to content

Code run with -c or a "code" launch gets __name__ == "builtins" instead of "__main__" #2079

Description

@Om-singhaI

Before creating a new issue, please check the FAQ to see if your question is answered there.

Environment data

  • debugpy version: 1.8.22+3.g751502d (running from source at 751502d, current main)
  • OS and version: macOS 26.6.2 (25G83)
  • Python version (& distribution if applicable, e.g. Anaconda): CPython 3.10.6 from python.org, and 3.14.3 from Homebrew
  • Using VS Code or Visual Studio: neither. I used the CLI, and also a "code" launch sent straight to the adapter.

Actual behavior

Code passed with -c, or with "code" in a launch configuration, doesn't run as __main__. __name__ is "builtins", so an if __name__ == "__main__": block is skipped, and a class the code defines can't be pickled:

__name__ = builtins
pickle failed: Can't pickle <class 'Point'>: attribute lookup Point on builtins failed

That is from 3.10.6. 3.14.3 gives the same result, with the pickle error worded as it's not found as builtins.Point.

run_code() in src/debugpy/server/cli.py ends with eval(code, {}). That globals dict has no __name__, so the name is looked up in builtins and comes back as "builtins". A class the code defines gets that as its __module__, which is where pickle then looks for it. A file target doesn't have this problem, because run_file() uses runpy.run_path(target, run_name="__main__"), which puts a temporary __main__ module in place.

A "code" launch goes through the same function, since the adapter turns it into a -c argument for the debugpy CLI in the debuggee. With "code": ["print('__name__ is', __name__)", "if __name__ == '__main__':", " print('main block ran')"] and "console": "internalConsole", the debuggee prints __name__ is builtins and nothing else.

Expected behavior

The same as python -c:

__name__ = __main__
main block ran
pickle ok

Steps to reproduce:

CODE='import pickle
class Point: pass
print("__name__ =", __name__)
if __name__ == "__main__":
    print("main block ran")
try:
    pickle.dumps(Point()); print("pickle ok")
except Exception as e:
    print("pickle failed:", e)'

python3 -c "$CODE"
python3 -m debugpy --listen 127.0.0.1:0 -c "$CODE"

The first command prints the expected output above and the second prints the actual output. Nothing needs to attach; the code runs right away because --wait-for-client isn't given.

I have a fix and can open a PR. It runs the code in a temporary __main__ module, the same way run_path() does for a file, and adds a test to tests/debugpy/server/test_cli.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions