Bug report
Bug description:
Python 3.14 changed argparse's default prog in gh-66436.
A non-__main__ __main__.__spec__.name is now rendered as a python -m invocation.
Our PAR executable bootstrap uses:
runpy._run_module_as_main(main_module, alter_argv=False)
This intentionally executes an importable module as __main__, while preserving the user-facing executable filename in sys.argv[0].
Python 3.14 instead reports a synthetic python -m module command that the user did not invoke.
This is an uncommon setup, but other executable loaders or embedded runtimes may use the same pattern.
Reproducer
# reproducer.py import argparse import os import runpy import sys if os.environ.pop("_ARGPARSE_REPRO_MAIN", None): print(argparse.ArgumentParser().prog) else: sys.argv[0] = "/tmp/my_tool.par" os.environ["_ARGPARSE_REPRO_MAIN"] = "1" runpy._run_module_as_main("reproduce", alter_argv=False)
With 3.14-3.16:
$ python3.1{4,5,6} reproduce.py python3.1{4,5,6} -m reproduce
With 3.12:
my_tool.par
The module is deliberately executed as __main__, so its populated __main__.__spec__ is expected.
The issue is that this alone does not prove the interpreter was invoked with -m.
A potential solution that preserves the intent of the original change is consulting sys.orig_argv to confirm an actual -m <module> invocation.
CPython versions tested on:
3.14, 3.15, CPython main branch
Operating systems tested on:
Linux
Linked PRs
- GH-155009: Preserve argparse prog for programmatic module-as-main execution #155199
- [3.15] GH-155009: Preserve argparse prog for programmatic module-as-main execution (GH-155199) #155200
- [3.14] GH-155009: Preserve argparse prog for programmatic module-as-main execution (GH-155199) #155201
- gh-155009: Fix argparse test on non-UTF-8 locales like Raspbian #155227
- [3.15] gh-155009: Fix argparse test on non-UTF-8 locales like Raspbian (GH-155227) #155270
- [3.14] gh-155009: Fix argparse test on non-UTF-8 locales like Raspbian (GH-155227) #155271