Skip to content
Open
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
3 changes: 2 additions & 1 deletion Include/errcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// the parser only returns E_EOF when it hits EOF immediately, and it
// never returns E_OK.
//
// The public PyRun_InteractiveOneObjectEx() function can return E_EOF,
// The public PyRun_InteractiveOneObject() function can return E_EOF,
// same as its variants:
//
// * PyRun_InteractiveOneObject()
Expand Down Expand Up @@ -38,6 +38,7 @@ extern "C" {
#define E_BADSINGLE 27 /* Ill-formed single statement input */
#define E_INTERACT_STOP 28 /* Interactive mode stopped tokenization */
#define E_COLUMNOVERFLOW 29 /* Column offset overflow */
#define E_EXITCODE 30 /* (internal) got SystemExit, use exitcode */

#ifdef __cplusplus
}
Expand Down
6 changes: 5 additions & 1 deletion Include/internal/pycore_pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ extern int _Py_LegacyLocaleDetected(int warn);
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);
extern int _PyRun_SimpleString(
const char *command,
const char* name,
PyCompilerFlags *flags,
int *exitcode);


/* interpreter config */
Expand Down
15 changes: 9 additions & 6 deletions Include/internal/pycore_pythonrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif

extern int _PyRun_SimpleFileObject(
extern int _PyRun_SimpleFile(
FILE *fp,
PyObject *filename,
int closeit,
PyCompilerFlags *flags);
PyCompilerFlags *flags,
int *exitcode);

extern int _PyRun_AnyFileObject(
extern int _PyRun_AnyFile(
FILE *fp,
PyObject *filename,
int closeit,
PyCompilerFlags *flags);
PyCompilerFlags *flags,
int *exitcode);

extern int _PyRun_InteractiveLoopObject(
extern int _PyRun_InteractiveLoop(
FILE *fp,
PyObject *filename,
PyCompilerFlags *flags);
PyCompilerFlags *flags,
int *exitcode);

extern int _PyObject_SupportedAsScript(PyObject *);
extern const char* _Py_SourceAsString(
Expand Down
56 changes: 50 additions & 6 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -590,6 +597,43 @@ 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_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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading