Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/debugpy/server/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,10 @@ def run_code():
log.describe_environment("Pre-launch environment:")
log.info("Running code:\n\n{0}", options.target)

eval(code, {})
# Like "python -c", run the code as the __main__ module, so that __name__ is
# "__main__" and the classes and functions it defines can be pickled.
with runpy._TempModule("__main__") as main_module:
eval(code, main_module.module.__dict__)
else:
log.error("No target to run.")

Expand Down
32 changes: 32 additions & 0 deletions tests/debugpy/server/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,3 +408,35 @@ def test_pep_768_remote_exec_called_with_backslash_path():
finally:
for attr, value in original_options.items():
setattr(cli.options, attr, value)


def test_run_code_runs_as_main(monkeypatch):
"""run_code() must run the -c code as the __main__ module, like "python -c" does,
so that __name__ == "__main__" checks pass and the classes it defines can be
pickled. It must also put the original __main__ module back afterwards."""
import types
from debugpy.server import cli

# The code records what it sees as attributes of this module.
result = types.ModuleType("debugpy_run_code_result")
monkeypatch.setitem(sys.modules, "debugpy_run_code_result", result)
monkeypatch.setattr(sys, "path", list(sys.path))
monkeypatch.setattr(cli, "start_debugging", lambda argv_0: None)
monkeypatch.setattr(
cli.options,
"target",
"import pickle, sys\n"
"import debugpy_run_code_result as result\n"
"class Point: pass\n"
"result.name = __name__\n"
"result.is_main = sys.modules['__main__'].__dict__ is globals()\n"
"result.unpickled = type(pickle.loads(pickle.dumps(Point()))) is Point\n",
)

original_main = sys.modules["__main__"]
cli.run_code()

assert result.name == "__main__"
assert result.is_main
assert result.unpickled
assert sys.modules["__main__"] is original_main
Loading