Skip to content

Commit 58a9939

Browse files
authored
bpo-44131: Py_FrozenMain() uses PyConfig_SetBytesArgv() (GH-26201)
Moreover, Py_FrozenMain() relies on Py_InitializeFromConfig() to handle the PYTHONUNBUFFERED environment variable and configure C stdio streams like stdout (make the stream unbuffered).
1 parent 4d396e7 commit 58a9939

File tree

4 files changed

+51
-100
lines changed

4 files changed

+51
-100
lines changed

Lib/test/test_embed.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,14 +1484,18 @@ def test_unicode_id_init(self):
14841484
@unittest.skipIf(os.name == 'nt',
14851485
'Py_FrozenMain is not exported on Windows')
14861486
def test_frozenmain(self):
1487-
out, err = self.run_embedded_interpreter("test_frozenmain")
1488-
exe = os.path.realpath('./argv0')
1487+
env = dict(os.environ)
1488+
env['PYTHONUNBUFFERED'] = '1'
1489+
out, err = self.run_embedded_interpreter("test_frozenmain", env=env)
1490+
executable = os.path.realpath('./argv0')
14891491
expected = textwrap.dedent(f"""
14901492
Frozen Hello World
14911493
sys.argv ['./argv0', '-E', 'arg1', 'arg2']
14921494
config program_name: ./argv0
1493-
config executable: {exe}
1495+
config executable: {executable}
14941496
config use_environment: 1
1497+
config configure_c_stdio: 1
1498+
config buffered_stdio: 0
14951499
""").lstrip()
14961500
self.assertEqual(out, expected)
14971501

Programs/test_frozenmain.h

Lines changed: 22 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Programs/test_frozenmain.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
print("Frozen Hello World")
88
print("sys.argv", sys.argv)
99
config = _testinternalcapi.get_configs()['config']
10-
print(f"config program_name: {config['program_name']}")
11-
print(f"config executable: {config['executable']}")
12-
print(f"config use_environment: {config['use_environment']}")
10+
for key in (
11+
'program_name',
12+
'executable',
13+
'use_environment',
14+
'configure_c_stdio',
15+
'buffered_stdio',
16+
):
17+
print(f"config {key}: {config[key]}")

Python/frozenmain.c

Lines changed: 14 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -20,74 +20,28 @@ Py_FrozenMain(int argc, char **argv)
2020
Py_ExitStatusException(status);
2121
}
2222

23-
const char *p;
24-
int i, n, sts = 1;
25-
int inspect = 0;
26-
int unbuffered = 0;
27-
char *oldloc = NULL;
28-
wchar_t **argv_copy = NULL;
29-
/* We need a second copies, as Python might modify the first one. */
30-
wchar_t **argv_copy2 = NULL;
31-
32-
if (argc > 0) {
33-
argv_copy = PyMem_RawMalloc(sizeof(wchar_t*) * argc);
34-
argv_copy2 = PyMem_RawMalloc(sizeof(wchar_t*) * argc);
35-
if (!argv_copy || !argv_copy2) {
36-
fprintf(stderr, "out of memory\n");
37-
goto error;
38-
}
39-
}
40-
4123
PyConfig config;
4224
PyConfig_InitPythonConfig(&config);
43-
config.pathconfig_warnings = 0; /* Suppress errors from getpath.c */
44-
45-
if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') {
46-
inspect = 1;
47-
}
48-
if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') {
49-
unbuffered = 1;
50-
}
51-
52-
if (unbuffered) {
53-
setbuf(stdin, (char *)NULL);
54-
setbuf(stdout, (char *)NULL);
55-
setbuf(stderr, (char *)NULL);
56-
}
25+
// Suppress errors from getpath.c
26+
config.pathconfig_warnings = 0;
27+
// Don't parse command line options like -E
28+
config.parse_argv = 0;
5729

58-
oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL));
59-
if (!oldloc) {
60-
fprintf(stderr, "out of memory\n");
61-
goto error;
30+
status = PyConfig_SetBytesArgv(&config, argc, argv);
31+
if (PyStatus_Exception(status)) {
32+
PyConfig_Clear(&config);
33+
Py_ExitStatusException(status);
6234
}
6335

64-
setlocale(LC_ALL, "");
65-
for (i = 0; i < argc; i++) {
66-
argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
67-
argv_copy2[i] = argv_copy[i];
68-
if (!argv_copy[i]) {
69-
fprintf(stderr,
70-
"Unable to decode the command line argument #%i\n",
71-
i + 1);
72-
argc = i;
73-
goto error;
74-
}
36+
const char *p;
37+
int inspect = 0;
38+
if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') {
39+
inspect = 1;
7540
}
76-
setlocale(LC_ALL, oldloc);
77-
PyMem_RawFree(oldloc);
78-
oldloc = NULL;
7941

8042
#ifdef MS_WINDOWS
8143
PyInitFrozenExtensions();
8244
#endif /* MS_WINDOWS */
83-
if (argc >= 1) {
84-
status = PyConfig_SetString(&config, &config.program_name,
85-
argv_copy[0]);
86-
if (PyStatus_Exception(status)) {
87-
PyConfig_Clear(&config);
88-
Py_ExitStatusException(status);
89-
}
90-
}
9145

9246
status = Py_InitializeFromConfig(&config);
9347
PyConfig_Clear(&config);
@@ -104,9 +58,8 @@ Py_FrozenMain(int argc, char **argv)
10458
Py_GetVersion(), Py_GetCopyright());
10559
}
10660

107-
PySys_SetArgv(argc, argv_copy);
108-
109-
n = PyImport_ImportFrozenModule("__main__");
61+
int sts = 1;
62+
int n = PyImport_ImportFrozenModule("__main__");
11063
if (n == 0) {
11164
Py_FatalError("the __main__ module is not frozen");
11265
}
@@ -128,14 +81,5 @@ Py_FrozenMain(int argc, char **argv)
12881
if (Py_FinalizeEx() < 0) {
12982
sts = 120;
13083
}
131-
132-
error:
133-
PyMem_RawFree(argv_copy);
134-
if (argv_copy2) {
135-
for (i = 0; i < argc; i++)
136-
PyMem_RawFree(argv_copy2[i]);
137-
PyMem_RawFree(argv_copy2);
138-
}
139-
PyMem_RawFree(oldloc);
14084
return sts;
14185
}

0 commit comments

Comments
 (0)