https://github.com/python/cpython/commit/fac72f1ee91fb380640a4c4be6df07a8f02d3110
commit: fac72f1ee91fb380640a4c4be6df07a8f02d3110
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-07-09T18:28:15Z
summary:
gh-152132: Fix Py_RunMain() to return an exit code (#153446)
* Fix Py_RunMain() to return an exit code, rather than calling
Py_Exit(), when running a script, a command, or the REPL.
* _PyRun_SimpleFile() now logs errors to stderr, for example if setting
__main__.__file__ fails.
* Add tests on Py_RunMain() exitcode.
* Rename functions:
* _PyRun_SimpleStringFlagsWithName() => _PyRun_SimpleString()
* _PyRun_SimpleFileObject() => _PyRun_SimpleFile()
* _PyRun_AnyFileObject() => _PyRun_AnyFile()
* _PyRun_InteractiveLoopObject() => _PyRun_InteractiveLoop()
* Change _PyRun_SimpleString(), _PyRun_SimpleFile(), _PyRun_AnyFile() and
_PyRun_InteractiveLoop() return type to PyObject*.
* pymain_repl() now displays the error if PySys_Audit() or
import _pyrepl failed.
* PyRun_SimpleFileExFlags() and PyRun_AnyFileExFlags() now log
PyUnicode_DecodeFSDefault() error. So these functions can no longer
return -1 with an exception set.
files:
A Misc/NEWS.d/next/C_API/2026-07-09-17-33-29.gh-issue-152132.Ma8MJZ.rst
M Include/internal/pycore_pylifecycle.h
M Include/internal/pycore_pythonrun.h
M Lib/test/test_embed.py
M Modules/_testcapi/run.c
M Modules/main.c
M Programs/_testembed.c
M Python/pythonrun.c
diff --git a/Include/internal/pycore_pylifecycle.h
b/Include/internal/pycore_pylifecycle.h
index 12e1e78526db35a..ab627c28c1fa5ee 100644
--- a/Include/internal/pycore_pylifecycle.h
+++ b/Include/internal/pycore_pylifecycle.h
@@ -108,9 +108,6 @@ extern int _Py_LegacyLocaleDetected(int warn);
// Export for 'readline' shared extension
PyAPI_FUNC(char*) _Py_SetLocaleFromEnv(int category);
-// Export for special main.c string compiling with source tracebacks
-int _PyRun_SimpleStringFlagsWithName(const char *command, const char* name,
PyCompilerFlags *flags);
-
/* interpreter config */
diff --git a/Include/internal/pycore_pythonrun.h
b/Include/internal/pycore_pythonrun.h
index 66dd7cd843b04fc..b2126f3f71bb46e 100644
--- a/Include/internal/pycore_pythonrun.h
+++ b/Include/internal/pycore_pythonrun.h
@@ -8,23 +8,18 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif
-extern int _PyRun_SimpleFileObject(
+extern PyObject* _PyRun_SimpleFile(
FILE *fp,
PyObject *filename,
int closeit,
PyCompilerFlags *flags);
-extern int _PyRun_AnyFileObject(
+extern PyObject* _PyRun_AnyFile(
FILE *fp,
PyObject *filename,
int closeit,
PyCompilerFlags *flags);
-extern int _PyRun_InteractiveLoopObject(
- FILE *fp,
- PyObject *filename,
- PyCompilerFlags *flags);
-
extern int _PyObject_SupportedAsScript(PyObject *);
extern const char* _Py_SourceAsString(
PyObject *cmd,
@@ -39,6 +34,12 @@ extern PyObject * _Py_CompileStringObjectWithModule(
PyCompilerFlags *flags, int optimize,
PyObject *module);
+// Export for special main.c string compiling with source tracebacks
+extern PyObject* _PyRun_SimpleString(
+ const char *command,
+ PyObject* name,
+ PyCompilerFlags *flags);
+
/* Stack size, in "pointers". This must be large enough, so
* no two calls to check recursion depth are more than this far
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index d0ac1d1c0cb1a15..40036609a190551 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -68,6 +68,9 @@
if not os.path.isfile(os.path.join(STDLIB_INSTALL, 'os.py')):
STDLIB_INSTALL = None
+CODE_EXITCODE_123 = 'raise SystemExit(123)'
+
+
def debug_build(program):
program = os.path.basename(program)
name = os.path.splitext(program)[0]
@@ -140,12 +143,16 @@ def run_embedded_interpreter(self, *args, env=None,
env = env.copy()
env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
- p = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- universal_newlines=True,
- env=env,
- cwd=cwd)
+ kwargs = dict(
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ env=env,
+ cwd=cwd,
+ )
+ if input is not None:
+ kwargs['stdin'] = subprocess.PIPE
+ p = subprocess.Popen(cmd, **kwargs)
try:
(out, err) = p.communicate(input=input, timeout=timeout)
except:
@@ -590,6 +597,55 @@ def _nogil_filtered_err(err: str, mod_name: str) -> str:
]
return "\n".join(filtered_err_lines)
+ def check_program_exitcode(self, *args, check_stderr=True, **kwargs):
+ out, err = self.run_embedded_interpreter(*args, **kwargs)
+ self.assertEqual(out.rstrip(), 'ok! Py_RunMain() returned 123')
+ if check_stderr:
+ self.assertEqual(err, '')
+
+ def test_init_run_main_code_exitcode(self):
+ code = CODE_EXITCODE_123
+ self.check_program_exitcode("test_init_run_main_code_exitcode", code)
+
+ def test_init_run_main_script_exitcode(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ filename = os.path.join(tmpdir, 'script.py')
+ with open(filename, 'w') as fp:
+ fp.write(CODE_EXITCODE_123)
+
+ self.check_program_exitcode("test_init_run_main_script_exitcode",
+ filename)
+
+ def test_init_run_main_interactive_exitcode(self):
+ code = CODE_EXITCODE_123
+ self.check_program_exitcode("test_init_run_main_interactive_exitcode",
+ input=code,
+ check_stderr=False)
+
+ def test_init_run_main_startup_exitcode(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ filename = os.path.join(tmpdir, 'startup.py')
+ with open(filename, 'x') as fp:
+ fp.write(CODE_EXITCODE_123)
+
+ env = dict(os.environ)
+ env['PYTHONSTARTUP'] = filename
+
self.check_program_exitcode("test_init_run_main_interactive_exitcode",
+ env=env,
+ check_stderr=False)
+
+ def test_init_run_main_module_exitcode(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ modname = '_testembed_testmodule'
+ filename = os.path.join(tmpdir, modname + '.py')
+ with open(filename, 'x', encoding='utf8') as fp:
+ fp.write(CODE_EXITCODE_123)
+
+ env = dict(os.environ)
+ env['PYTHONPATH'] = tmpdir
+ self.check_program_exitcode("test_init_run_main_module_exitcode",
+ modname, env=env)
+
def config_dev_mode(preconfig, config):
preconfig['allocator'] = PYMEM_ALLOCATOR_DEBUG
diff --git
a/Misc/NEWS.d/next/C_API/2026-07-09-17-33-29.gh-issue-152132.Ma8MJZ.rst
b/Misc/NEWS.d/next/C_API/2026-07-09-17-33-29.gh-issue-152132.Ma8MJZ.rst
new file mode 100644
index 000000000000000..633c41e40bf2696
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2026-07-09-17-33-29.gh-issue-152132.Ma8MJZ.rst
@@ -0,0 +1,3 @@
+Fix :c:func:`Py_RunMain` to return an exit code, rather than calling
+:c:func:`Py_Exit`, when running a script, a command, or the REPL. Patch by
+Victor Stinner.
diff --git a/Modules/_testcapi/run.c b/Modules/_testcapi/run.c
index 4a63c0f98a74d4c..7fc180e136559ba 100644
--- a/Modules/_testcapi/run.c
+++ b/Modules/_testcapi/run.c
@@ -129,9 +129,6 @@ run_simplefile(PyObject *mod, PyObject *args)
assert(_Py_IsValidFD(fd));
fclose(fp);
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -203,9 +200,6 @@ run_simplefileex(PyObject *mod, PyObject *args)
return NULL;
}
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -243,9 +237,6 @@ run_simplefileexflags(PyObject *mod, PyObject *args)
return NULL;
}
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -272,9 +263,6 @@ run_anyfile(PyObject *mod, PyObject *args)
assert(_Py_IsValidFD(fd));
fclose(fp);
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -310,9 +298,6 @@ run_anyfileflags(PyObject *mod, PyObject *args)
assert(_Py_IsValidFD(fd));
fclose(fp);
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -342,9 +327,6 @@ run_anyfileex(PyObject *mod, PyObject *args)
return NULL;
}
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -382,9 +364,6 @@ run_anyfileexflags(PyObject *mod, PyObject *args)
return NULL;
}
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -664,9 +643,6 @@ run_simplestring(PyObject *mod, PyObject *args)
}
int res = PyRun_SimpleString(str);
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
@@ -689,9 +665,6 @@ run_simplestringflags(PyObject *mod, PyObject *args)
}
int res = PyRun_SimpleStringFlags(str, pflags);
- if (res == -1 && PyErr_Occurred()) {
- return NULL;
- }
assert(!PyErr_Occurred());
return PyLong_FromLong(res);
}
diff --git a/Modules/main.c b/Modules/main.c
index 2f23513cc2403dc..a65247dc1e5c617 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -10,7 +10,7 @@
#include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0()
#include "pycore_pylifecycle.h" // _Py_PreInitializeFromPyArgv()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
-#include "pycore_pythonrun.h" // _PyRun_AnyFileObject()
+#include "pycore_pythonrun.h" // _PyRun_AnyFile()
#include "pycore_tuple.h" // _PyTuple_FromPair
#include "pycore_unicodeobject.h" // _PyUnicode_Dedent()
@@ -235,7 +235,6 @@ static int
pymain_run_command(wchar_t *command)
{
PyObject *unicode, *bytes;
- int ret;
unicode = PyUnicode_FromWideChar(command, -1);
if (unicode == NULL) {
@@ -259,9 +258,18 @@ pymain_run_command(wchar_t *command)
PyCompilerFlags cf = _PyCompilerFlags_INIT;
cf.cf_flags |= PyCF_IGNORE_COOKIE;
- ret = _PyRun_SimpleStringFlagsWithName(PyBytes_AsString(bytes),
"<string>", &cf);
+ PyObject *result = NULL;
+ PyObject *string_obj = PyUnicode_FromString("<string>");
+ if (string_obj != NULL) {
+ result = _PyRun_SimpleString(PyBytes_AsString(bytes),
+ string_obj, &cf);
+ Py_DECREF(string_obj);
+ }
Py_DECREF(bytes);
- return (ret != 0);
+ if (result == NULL) {
+ return pymain_exit_err_print();
+ }
+ return 0;
error:
PySys_WriteStderr("Unable to decode the command from the command line:\n");
@@ -270,9 +278,9 @@ pymain_run_command(wchar_t *command)
static int
-pymain_start_pyrepl(int pythonstartup)
+pymain_run_pyrepl(int pythonstartup)
{
- int res = 0;
+ int exitcode = 0;
PyObject *console = NULL;
PyObject *empty_tuple = NULL;
PyObject *kwargs = NULL;
@@ -282,37 +290,41 @@ pymain_start_pyrepl(int pythonstartup)
PyObject *pyrepl = PyImport_ImportModule("_pyrepl.main");
if (pyrepl == NULL) {
fprintf(stderr, "Could not import _pyrepl.main\n");
- res = pymain_exit_err_print();
- goto done;
+ goto error;
}
+
console = PyObject_GetAttrString(pyrepl, "interactive_console");
if (console == NULL) {
fprintf(stderr, "Could not access _pyrepl.main.interactive_console\n");
- res = pymain_exit_err_print();
- goto done;
+ goto error;
}
+
empty_tuple = PyTuple_New(0);
if (empty_tuple == NULL) {
- res = pymain_exit_err_print();
- goto done;
+ goto error;
}
kwargs = PyDict_New();
if (kwargs == NULL) {
- res = pymain_exit_err_print();
- goto done;
+ goto error;
}
+
main_module = PyImport_AddModuleRef("__main__");
if (main_module == NULL) {
- res = pymain_exit_err_print();
- goto done;
+ goto error;
}
- if (!PyDict_SetItemString(kwargs, "mainmodule", main_module)
- && !PyDict_SetItemString(kwargs, "pythonstartup", pythonstartup ?
Py_True : Py_False)) {
- console_result = PyObject_Call(console, empty_tuple, kwargs);
- if (console_result == NULL) {
- res = pymain_exit_err_print();
- }
+
+ PyObject *value = pythonstartup ? Py_True : Py_False;
+ if (PyDict_SetItemString(kwargs, "mainmodule", main_module)
+ || PyDict_SetItemString(kwargs, "pythonstartup", value))
+ {
+ goto error;
+ }
+
+ console_result = PyObject_Call(console, empty_tuple, kwargs);
+ if (console_result == NULL) {
+ goto error;
}
+
done:
Py_XDECREF(console_result);
Py_XDECREF(kwargs);
@@ -320,7 +332,11 @@ pymain_start_pyrepl(int pythonstartup)
Py_XDECREF(console);
Py_XDECREF(pyrepl);
Py_XDECREF(main_module);
- return res;
+ return exitcode;
+
+error:
+ exitcode = pymain_exit_err_print();
+ goto done;
}
@@ -406,10 +422,14 @@ pymain_run_file_obj(PyObject *program_name, PyObject
*filename,
return pymain_exit_err_print();
}
- /* PyRun_AnyFileExFlags(closeit=1) calls fclose(fp) before running code */
+ /* _PyRun_AnyFile(closeit=1) calls fclose(fp) before running code */
PyCompilerFlags cf = _PyCompilerFlags_INIT;
- int run = _PyRun_AnyFileObject(fp, filename, 1, &cf);
- return (run != 0);
+ PyObject *result = _PyRun_AnyFile(fp, filename, 1, &cf);
+ if (result == NULL) {
+ return pymain_exit_err_print();
+ }
+ Py_DECREF(result);
+ return 0;
}
static int
@@ -417,28 +437,26 @@ pymain_run_file(const PyConfig *config)
{
PyObject *filename = PyUnicode_FromWideChar(config->run_filename, -1);
if (filename == NULL) {
- PyErr_Print();
- return -1;
+ return pymain_exit_err_print();
}
PyObject *program_name = PyUnicode_FromWideChar(config->program_name, -1);
if (program_name == NULL) {
Py_DECREF(filename);
- PyErr_Print();
- return -1;
+ return pymain_exit_err_print();
}
- int res = pymain_run_file_obj(program_name, filename,
- config->skip_source_first_line);
+ int exitcode = pymain_run_file_obj(program_name, filename,
+ config->skip_source_first_line);
Py_DECREF(filename);
Py_DECREF(program_name);
- return res;
+ return exitcode;
}
static int
pymain_run_startup(PyConfig *config, int *exitcode)
{
- int ret;
+ int ret = 0;
if (!config->use_environment) {
return 0;
}
@@ -478,10 +496,12 @@ pymain_run_startup(PyConfig *config, int *exitcode)
}
PyCompilerFlags cf = _PyCompilerFlags_INIT;
- (void) _PyRun_SimpleFileObject(fp, startup, 0, &cf);
- PyErr_Clear();
+ PyObject *result = _PyRun_SimpleFile(fp, startup, 0, &cf);
fclose(fp);
- ret = 0;
+ if (result == NULL) {
+ goto error;
+ }
+ Py_DECREF(result);
done:
Py_XDECREF(startup);
@@ -542,22 +562,8 @@ pymain_set_inspect(PyConfig *config, int inspect)
static int
-pymain_run_stdin(PyConfig *config)
+_pymain_run_repl(PyConfig *config, int startup)
{
- if (stdin_is_interactive(config)) {
- // do exit on SystemExit
- pymain_set_inspect(config, 0);
-
- int exitcode;
- if (pymain_run_startup(config, &exitcode)) {
- return exitcode;
- }
-
- if (pymain_run_interactive_hook(&exitcode)) {
- return exitcode;
- }
- }
-
/* call pending calls like signal handlers (SIGINT) */
if (Py_MakePendingCalls() == -1) {
return pymain_exit_err_print();
@@ -567,14 +573,13 @@ pymain_run_stdin(PyConfig *config)
return pymain_exit_err_print();
}
- int run;
if (isatty(fileno(stdin))
&& !_Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) {
PyObject *pyrepl = PyImport_ImportModule("_pyrepl");
if (pyrepl != NULL) {
- run = pymain_start_pyrepl(0);
+ int exitcode = pymain_run_pyrepl(startup);
Py_DECREF(pyrepl);
- return run;
+ return exitcode;
}
if (!PyErr_ExceptionMatches(PyExc_ModuleNotFoundError)) {
fprintf(stderr, "Could not import _pyrepl.main\n");
@@ -584,8 +589,37 @@ pymain_run_stdin(PyConfig *config)
}
PyCompilerFlags cf = _PyCompilerFlags_INIT;
- run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf);
- return (run != 0);
+ PyObject *result = NULL;
+ PyObject *stdin_obj = PyUnicode_FromString("<stdin>");
+ if (stdin_obj != NULL) {
+ result = _PyRun_AnyFile(stdin, stdin_obj, 0, &cf);
+ Py_DECREF(stdin_obj);
+ }
+ if (result == NULL) {
+ return pymain_exit_err_print();
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+static int
+pymain_run_stdin(PyConfig *config)
+{
+ if (stdin_is_interactive(config)) {
+ // do exit on SystemExit
+ pymain_set_inspect(config, 0);
+
+ int exitcode;
+ if (pymain_run_startup(config, &exitcode)) {
+ return exitcode;
+ }
+
+ if (pymain_run_interactive_hook(&exitcode)) {
+ return exitcode;
+ }
+ }
+
+ return _pymain_run_repl(config, 0);
}
@@ -607,30 +641,7 @@ pymain_repl(PyConfig *config, int *exitcode)
return;
}
- if (PySys_Audit("cpython.run_stdin", NULL) < 0) {
- return;
- }
-
- if (isatty(fileno(stdin))
- && !_Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) {
- PyObject *pyrepl = PyImport_ImportModule("_pyrepl");
- if (pyrepl != NULL) {
- int run = pymain_start_pyrepl(1);
- *exitcode = (run != 0);
- Py_DECREF(pyrepl);
- return;
- }
- if (!PyErr_ExceptionMatches(PyExc_ModuleNotFoundError)) {
- PyErr_Clear();
- fprintf(stderr, "Could not import _pyrepl.main\n");
- return;
- }
- PyErr_Clear();
- }
- PyCompilerFlags cf = _PyCompilerFlags_INIT;
- int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf);
- *exitcode = (run != 0);
- return;
+ *exitcode = _pymain_run_repl(config, 1);
}
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 74d0fe37ddaeadb..418609abc5f6b82 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -64,6 +64,53 @@ static void error(const char *msg)
}
+static void error_fmt(const char *format, ...)
+{
+ va_list vargs;
+ va_start(vargs, format);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, format, vargs);
+ fprintf(stderr, "\n");
+ va_end(vargs);
+ fflush(stderr);
+}
+
+
+static wchar_t* py_getenv(const char *name)
+{
+ const char *env = getenv(name);
+ if (env == NULL) {
+ error_fmt("need %s env var", name);
+ return NULL;
+ }
+
+ wchar_t *result = Py_DecodeLocale(env, NULL);
+ if (result == NULL) {
+ error("Py_DecodeLocale() failed");
+ return NULL;
+ }
+ return result;
+}
+
+
+static wchar_t* get_cmdline_arg(const char *arg_name)
+{
+ if (main_argc < 3) {
+ const char *test = main_argv[1];
+ fprintf(stderr, "usage: %s %s %s\n", PROGRAM, test, arg_name);
+ return NULL;
+ }
+ const char *arg = main_argv[2];
+
+ wchar_t *result = Py_DecodeLocale(arg, NULL);
+ if (result == NULL) {
+ error_fmt("failed to decode %s command line argument", arg_name);
+ return NULL;
+ }
+ return result;
+}
+
+
static void config_set_string(PyConfig *config, wchar_t **config_str, const
wchar_t *str)
{
PyStatus status = PyConfig_SetString(config, config_str, str);
@@ -1564,14 +1611,8 @@ static int test_init_sys_add(void)
static int test_init_setpath(void)
{
- char *env = getenv("TESTPATH");
- if (!env) {
- error("missing TESTPATH env var");
- return 1;
- }
- wchar_t *path = Py_DecodeLocale(env, NULL);
+ wchar_t *path = py_getenv("TESTPATH");
if (path == NULL) {
- error("failed to decode TESTPATH");
return 1;
}
Py_SetPath(path);
@@ -1597,14 +1638,8 @@ static int test_init_setpath_config(void)
Py_ExitStatusException(status);
}
- char *env = getenv("TESTPATH");
- if (!env) {
- error("missing TESTPATH env var");
- return 1;
- }
- wchar_t *path = Py_DecodeLocale(env, NULL);
+ wchar_t *path = py_getenv("TESTPATH");
if (path == NULL) {
- error("failed to decode TESTPATH");
return 1;
}
Py_SetPath(path);
@@ -1626,14 +1661,8 @@ static int test_init_setpath_config(void)
static int test_init_setpythonhome(void)
{
- char *env = getenv("TESTHOME");
- if (!env) {
- error("missing TESTHOME env var");
- return 1;
- }
- wchar_t *home = Py_DecodeLocale(env, NULL);
+ wchar_t *home = py_getenv("TESTHOME");
if (home == NULL) {
- error("failed to decode TESTHOME");
return 1;
}
Py_SetPythonHome(home);
@@ -1651,14 +1680,8 @@ static int test_init_is_python_build(void)
{
// gh-91985: in-tree builds fail to check for build directory landmarks
// under the effect of 'home' or PYTHONHOME environment variable.
- char *env = getenv("TESTHOME");
- if (!env) {
- error("missing TESTHOME env var");
- return 1;
- }
- wchar_t *home = Py_DecodeLocale(env, NULL);
+ wchar_t *home = py_getenv("TESTHOME");
if (home == NULL) {
- error("failed to decode TESTHOME");
return 1;
}
@@ -1672,7 +1695,7 @@ static int test_init_is_python_build(void)
// Use an impossible value so we can detect whether it isn't updated
// during initialization.
config._is_python_build = INT_MAX;
- env = getenv("NEGATIVE_ISPYTHONBUILD");
+ char *env = getenv("NEGATIVE_ISPYTHONBUILD");
if (env && strcmp(env, "0") != 0) {
config._is_python_build = INT_MIN;
}
@@ -1997,6 +2020,82 @@ static int test_init_run_main(void)
}
+static int test_init_run_main_exitcode(Py_ssize_t argc, wchar_t * const *argv)
+{
+ PyConfig config;
+ PyConfig_InitPythonConfig(&config);
+
+ config.parse_argv = 1;
+ config_set_argv(&config, argc, argv);
+ config_set_string(&config, &config.program_name, L"./python3");
+
+ init_from_config_clear(&config);
+
+ int exitcode = Py_RunMain();
+ if (exitcode != 123) {
+ error_fmt("Py_RunMain() returned %i, expected 123", exitcode);
+ return 1;
+ }
+
+ // If Py_RunMain() calls Py_Exit(), this message is not written to stdout
+ printf("ok! Py_RunMain() returned 123\n");
+
+ return 0;
+}
+
+
+static int test_init_run_main_script_exitcode(void)
+{
+ wchar_t *filename = get_cmdline_arg("FILENAME");
+ if (filename == NULL) {
+ return 1;
+ }
+
+ wchar_t* argv[] = {L"python3", filename};
+ int res = test_init_run_main_exitcode(Py_ARRAY_LENGTH(argv), argv);
+ PyMem_RawFree(filename);
+
+ return res;
+}
+
+
+static int test_init_run_main_module_exitcode(void)
+{
+ wchar_t *module = get_cmdline_arg("MODULE");
+ if (module == NULL) {
+ return 1;
+ }
+
+ wchar_t* argv[] = {L"python3", L"-m", module};
+ int res = test_init_run_main_exitcode(Py_ARRAY_LENGTH(argv), argv);
+ PyMem_RawFree(module);
+
+ return res;
+}
+
+
+static int test_init_run_main_interactive_exitcode(void)
+{
+ wchar_t* argv[] = {L"python3", L"-i"};
+ return test_init_run_main_exitcode(Py_ARRAY_LENGTH(argv), argv);
+}
+
+
+static int test_init_run_main_code_exitcode(void)
+{
+ wchar_t *code = get_cmdline_arg("CODE");
+ if (code == NULL) {
+ return 1;
+ }
+
+ wchar_t* argv[] = {L"python3", L"-c", code};
+ int res = test_init_run_main_exitcode(Py_ARRAY_LENGTH(argv), argv);
+ PyMem_RawFree(code);
+
+ return res;
+}
+
+
static int test_init_main(void)
{
PyConfig config;
@@ -2938,6 +3037,10 @@ static struct TestCase TestCases[] = {
{"test_preinit_parse_argv", test_preinit_parse_argv},
{"test_preinit_dont_parse_argv", test_preinit_dont_parse_argv},
{"test_init_run_main", test_init_run_main},
+ {"test_init_run_main_code_exitcode", test_init_run_main_code_exitcode},
+ {"test_init_run_main_script_exitcode", test_init_run_main_script_exitcode},
+ {"test_init_run_main_module_exitcode", test_init_run_main_module_exitcode},
+ {"test_init_run_main_interactive_exitcode",
test_init_run_main_interactive_exitcode},
{"test_init_main", test_init_main},
{"test_init_sys_add", test_init_sys_add},
{"test_init_setpath", test_init_setpath},
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 8c441e7ebd3c79f..e0a752720dfcc2d 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -22,7 +22,7 @@
#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
#include "pycore_pylifecycle.h" // _Py_FdIsInteractive()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
-#include "pycore_pythonrun.h" // export _PyRun_InteractiveLoopObject()
+#include "pycore_pythonrun.h" // export _PyRun_AnyFile()
#include "pycore_sysmodule.h" // _PySys_SetAttr()
#include "pycore_traceback.h" // _PyTraceBack_Print()
#include "pycore_unicodeobject.h" // _PyUnicode_Equal()
@@ -41,50 +41,53 @@
# include "windows.h"
#endif
-/* Forward */
+/* Forward declarations */
static void flush_io(void);
static PyObject *run_mod(mod_ty, PyObject *, PyObject *, PyObject *,
PyCompilerFlags *, PyArena *, PyObject*, int);
static PyObject *run_pyc_file(FILE *, PyObject *, PyObject *,
PyCompilerFlags *);
-static int PyRun_InteractiveOneObjectEx(FILE *, PyObject *, PyCompilerFlags *);
-static PyObject* pyrun_file(FILE *fp, PyObject *filename, int start,
- PyObject *globals, PyObject *locals, int closeit,
- PyCompilerFlags *flags);
static PyObject *
-_PyRun_StringFlagsWithName(const char *str, PyObject* name, int start,
- PyObject *globals, PyObject *locals,
PyCompilerFlags *flags,
- int generate_new_source);
+_PyRun_String(const char *str, PyObject* name, int start,
+ PyObject *globals, PyObject *locals, PyCompilerFlags *flags,
+ int generate_new_source);
+static PyObject*
+_PyRun_InteractiveLoop(FILE *fp, PyObject *filename, PyCompilerFlags *flags);
+static int
+_PyRun_InteractiveOne(FILE *fp, PyObject *filename, PyCompilerFlags *flags);
+static PyObject *
+_PyRun_File(FILE *fp, PyObject *filename, int start, PyObject *globals,
+ PyObject *locals, int closeit, PyCompilerFlags *flags);
-int
-_PyRun_AnyFileObject(FILE *fp, PyObject *filename, int closeit,
- PyCompilerFlags *flags)
+
+PyObject*
+_PyRun_AnyFile(FILE *fp, PyObject *filename, int closeit,
+ PyCompilerFlags *flags)
{
int decref_filename = 0;
if (filename == NULL) {
filename = PyUnicode_FromString("???");
if (filename == NULL) {
- PyErr_Print();
- return -1;
+ return NULL;
}
decref_filename = 1;
}
- int res;
+ PyObject *result;
if (_Py_FdIsInteractive(fp, filename)) {
- res = _PyRun_InteractiveLoopObject(fp, filename, flags);
+ result = _PyRun_InteractiveLoop(fp, filename, flags);
if (closeit) {
fclose(fp);
}
}
else {
- res = _PyRun_SimpleFileObject(fp, filename, closeit, flags);
+ result = _PyRun_SimpleFile(fp, filename, closeit, flags);
}
if (decref_filename) {
Py_DECREF(filename);
}
- return res;
+ return result;
}
int
@@ -99,14 +102,20 @@ PyRun_AnyFileExFlags(FILE *fp, const char *filename, int
closeit,
return -1;
}
}
- int res = _PyRun_AnyFileObject(fp, filename_obj, closeit, flags);
+
+ PyObject *result = _PyRun_AnyFile(fp, filename_obj, closeit, flags);
Py_XDECREF(filename_obj);
- return res;
+ if (result == NULL) {
+ PyErr_Print();
+ return -1;
+ }
+ Py_DECREF(result);
+ return 0;
}
-int
-_PyRun_InteractiveLoopObject(FILE *fp, PyObject *filename, PyCompilerFlags
*flags)
+static PyObject*
+_PyRun_InteractiveLoop(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
{
PyCompilerFlags local_flags = _PyCompilerFlags_INIT;
if (flags == NULL) {
@@ -115,8 +124,7 @@ _PyRun_InteractiveLoopObject(FILE *fp, PyObject *filename,
PyCompilerFlags *flag
PyObject *v;
if (PySys_GetOptionalAttr(&_Py_ID(ps1), &v) < 0) {
- PyErr_Print();
- return -1;
+ return NULL;
}
if (v == NULL) {
v = PyUnicode_FromString(">>> ");
@@ -129,8 +137,7 @@ _PyRun_InteractiveLoopObject(FILE *fp, PyObject *filename,
PyCompilerFlags *flag
}
Py_XDECREF(v);
if (PySys_GetOptionalAttr(&_Py_ID(ps2), &v) < 0) {
- PyErr_Print();
- return -1;
+ return NULL;
}
if (v == NULL) {
v = PyUnicode_FromString("... ");
@@ -146,36 +153,41 @@ _PyRun_InteractiveLoopObject(FILE *fp, PyObject
*filename, PyCompilerFlags *flag
#ifdef Py_REF_DEBUG
int show_ref_count = _Py_GetConfig()->show_ref_count;
#endif
- int err = 0;
- int ret;
int nomem_count = 0;
+ int ret;
do {
- ret = PyRun_InteractiveOneObjectEx(fp, filename, flags);
+ ret = _PyRun_InteractiveOne(fp, filename, flags);
if (ret == -1 && PyErr_Occurred()) {
/* Prevent an endless loop after multiple consecutive MemoryErrors
* while still allowing an interactive command to fail with a
* MemoryError. */
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
if (++nomem_count > 16) {
- PyErr_Clear();
- err = -1;
- break;
+ return NULL;
}
} else {
nomem_count = 0;
}
+
+ int inspect = _Py_GetConfig()->inspect;
+ if (!inspect && PyErr_ExceptionMatches(PyExc_SystemExit)) {
+ return NULL;
+ }
+
PyErr_Print();
- flush_io();
- } else {
+ }
+ else {
nomem_count = 0;
}
+
#ifdef Py_REF_DEBUG
if (show_ref_count) {
_PyDebug_PrintTotalRefs();
}
#endif
} while (ret != E_EOF);
- return err;
+
+ Py_RETURN_NONE;
}
@@ -188,10 +200,14 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char
*filename, PyCompilerFlags *flag
return -1;
}
- int err = _PyRun_InteractiveLoopObject(fp, filename_obj, flags);
+ PyObject *result = _PyRun_InteractiveLoop(fp, filename_obj, flags);
Py_DECREF(filename_obj);
- return err;
-
+ if (result == NULL) {
+ PyErr_Print();
+ return -1;
+ }
+ Py_DECREF(result);
+ return 0;
}
@@ -287,8 +303,7 @@ pyrun_one_parse_ast(FILE *fp, PyObject *filename,
/* A PyRun_InteractiveOneObject() auxiliary function that does not print the
* error on failure. */
static int
-PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename,
- PyCompilerFlags *flags)
+_PyRun_InteractiveOne(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
{
if (!PyUnicode_Check(filename)) {
PyErr_Format(PyExc_TypeError, "expect str for filename, got %T",
@@ -362,9 +377,7 @@ PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename,
int
PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags
*flags)
{
- int res;
-
- res = PyRun_InteractiveOneObjectEx(fp, filename, flags);
+ int res = _PyRun_InteractiveOne(fp, filename, flags);
if (res == -1) {
PyErr_Print();
flush_io();
@@ -464,24 +477,26 @@ set_main_loader(PyObject *d, PyObject *filename, const
char *loader_name)
}
-int
-_PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit,
- PyCompilerFlags *flags)
+PyObject*
+_PyRun_SimpleFile(FILE *fp, PyObject *filename, int closeit,
+ PyCompilerFlags *flags)
{
- int ret = -1;
+ PyObject *res = NULL;
+ int set_file_name = 0;
PyObject *main_module = PyImport_AddModuleRef("__main__");
- if (main_module == NULL)
- return -1;
+ if (main_module == NULL) {
+ goto done;
+ }
PyObject *dict = PyModule_GetDict(main_module); // borrowed ref
- int set_file_name = 0;
int has_file = PyDict_ContainsString(dict, "__file__");
if (has_file < 0) {
goto done;
}
if (!has_file) {
if (PyDict_SetItemString(dict, "__file__", filename) < 0) {
+ fprintf(stderr, "python: failed to set __main__.__file__\n");
goto done;
}
set_file_name = 1;
@@ -492,7 +507,6 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int
closeit,
goto done;
}
- PyObject *v;
if (pyc) {
FILE *pyc_fp;
/* Try to run a pyc file. First, re-open in binary */
@@ -508,39 +522,31 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int
closeit,
if (set_main_loader(dict, filename, "SourcelessFileLoader") < 0) {
fprintf(stderr, "python: failed to set __main__.__loader__\n");
- ret = -1;
fclose(pyc_fp);
goto done;
}
- v = run_pyc_file(pyc_fp, dict, dict, flags);
+ res = run_pyc_file(pyc_fp, dict, dict, flags);
} else {
/* When running from stdin, leave __main__.__loader__ alone */
if ((!PyUnicode_Check(filename) || !PyUnicode_EqualToUTF8(filename,
"<stdin>")) &&
set_main_loader(dict, filename, "SourceFileLoader") < 0) {
fprintf(stderr, "python: failed to set __main__.__loader__\n");
- ret = -1;
goto done;
}
- v = pyrun_file(fp, filename, Py_file_input, dict, dict,
- closeit, flags);
+ res = _PyRun_File(fp, filename, Py_file_input, dict, dict,
+ closeit, flags);
}
flush_io();
- if (v == NULL) {
- Py_CLEAR(main_module);
- PyErr_Print();
- goto done;
- }
- Py_DECREF(v);
- ret = 0;
done:
if (set_file_name) {
if (PyDict_PopString(dict, "__file__", NULL) < 0) {
- PyErr_Print();
+ fprintf(stderr, "python: failed to delete __main__.__file__\n");
+ Py_CLEAR(res);
}
}
Py_XDECREF(main_module);
- return ret;
+ return res;
}
@@ -550,51 +556,47 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename,
int closeit,
{
PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename);
if (filename_obj == NULL) {
+ PyErr_Print();
return -1;
}
- int res = _PyRun_SimpleFileObject(fp, filename_obj, closeit, flags);
+ PyObject *result = _PyRun_SimpleFile(fp, filename_obj, closeit, flags);
Py_DECREF(filename_obj);
- return res;
+ if (result == NULL) {
+ PyErr_Print();
+ return -1;
+ }
+ return 0;
}
-int
-_PyRun_SimpleStringFlagsWithName(const char *command, const char* name,
PyCompilerFlags *flags) {
+PyObject*
+_PyRun_SimpleString(const char *command, PyObject* name,
+ PyCompilerFlags *flags)
+{
PyObject *main_module = PyImport_AddModuleRef("__main__");
if (main_module == NULL) {
- return -1;
+ return NULL;
}
PyObject *dict = PyModule_GetDict(main_module); // borrowed ref
- PyObject *res = NULL;
- if (name == NULL) {
- res = PyRun_StringFlags(command, Py_file_input, dict, dict, flags);
- } else {
- PyObject* the_name = PyUnicode_FromString(name);
- if (!the_name) {
- PyErr_Print();
- Py_DECREF(main_module);
- return -1;
- }
- res = _PyRun_StringFlagsWithName(command, the_name, Py_file_input,
dict, dict, flags, 0);
- Py_DECREF(the_name);
- }
+ PyObject *res = _PyRun_String(command, name, Py_file_input,
+ dict, dict, flags, 0);
Py_DECREF(main_module);
+ return res;
+}
+
+int
+PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
+{
+ PyObject *res = _PyRun_SimpleString(command, NULL, flags);
if (res == NULL) {
PyErr_Print();
return -1;
}
-
Py_DECREF(res);
return 0;
}
-int
-PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
-{
- return _PyRun_SimpleStringFlagsWithName(command, NULL, flags);
-}
-
static int
parse_exit_code(PyObject *code, int *exitcode_p)
{
@@ -1231,9 +1233,9 @@ void PyErr_DisplayException(PyObject *exc)
}
static PyObject *
-_PyRun_StringFlagsWithName(const char *str, PyObject* name, int start,
- PyObject *globals, PyObject *locals,
PyCompilerFlags *flags,
- int generate_new_source)
+_PyRun_String(const char *str, PyObject* name, int start,
+ PyObject *globals, PyObject *locals, PyCompilerFlags *flags,
+ int generate_new_source)
{
PyObject *ret = NULL;
mod_ty mod;
@@ -1277,12 +1279,12 @@ PyObject *
PyRun_StringFlags(const char *str, int start, PyObject *globals,
PyObject *locals, PyCompilerFlags *flags) {
- return _PyRun_StringFlagsWithName(str, NULL, start, globals, locals,
flags, 0);
+ return _PyRun_String(str, NULL, start, globals, locals, flags, 0);
}
static PyObject *
-pyrun_file(FILE *fp, PyObject *filename, int start, PyObject *globals,
- PyObject *locals, int closeit, PyCompilerFlags *flags)
+_PyRun_File(FILE *fp, PyObject *filename, int start, PyObject *globals,
+ PyObject *locals, int closeit, PyCompilerFlags *flags)
{
PyArena *arena = _PyArena_New();
if (arena == NULL) {
@@ -1316,11 +1318,12 @@ PyRun_FileExFlags(FILE *fp, const char *filename, int
start, PyObject *globals,
{
PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename);
if (filename_obj == NULL) {
+ PyErr_Print();
return NULL;
}
- PyObject *res = pyrun_file(fp, filename_obj, start, globals,
- locals, closeit, flags);
+ PyObject *res = _PyRun_File(fp, filename_obj, start, globals,
+ locals, closeit, flags);
Py_DECREF(filename_obj);
return res;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]