Skip to content

Commit b6d98c1

Browse files
authored
bpo-32381: Fix PyRun_SimpleFileExFlags() encoding (pythonGH-23642)
Fix encoding name when running a ".pyc" file on Windows: PyRun_SimpleFileExFlags() now uses the correct encoding to decode the filename. * Add pyrun_file() subfunction. * Add pyrun_simple_file() subfunction. * PyRun_SimpleFileExFlags() now calls _Py_fopen_obj() rather than _Py_fopen().
1 parent 233fddf commit b6d98c1

2 files changed

Lines changed: 135 additions & 92 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix encoding name when running a ``.pyc`` file on Windows:
2+
:c:func:`PyRun_SimpleFileExFlags()` now uses the correct encoding to decode
3+
the filename.

‎Python/pythonrun.c‎

Lines changed: 132 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,13 @@ extern "C" {
5959
static void flush_io(void);
6060
static PyObject *run_mod(mod_ty, PyObject *, PyObject *, PyObject *,
6161
PyCompilerFlags *, PyArena *);
62-
static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *,
62+
static PyObject *run_pyc_file(FILE *, PyObject *, PyObject *,
6363
PyCompilerFlags *);
6464
static int PyRun_InteractiveOneObjectEx(FILE *, PyObject *, PyCompilerFlags *);
65+
static PyObject* pyrun_file(FILE *fp, PyObject *filename, int start,
66+
PyObject *globals, PyObject *locals, int closeit,
67+
PyCompilerFlags *flags);
68+
6569

6670
/* Parse input from a file and execute it */
6771
int
@@ -269,82 +273,89 @@ PyRun_InteractiveOneFlags(FILE *fp, const char *filename_str, PyCompilerFlags *f
269273
the file type, and, if we may close it, at the first few bytes. */
270274

271275
static int
272-
maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit)
276+
maybe_pyc_file(FILE *fp, PyObject *filename, int closeit)
273277
{
274-
if (strcmp(ext, ".pyc") == 0)
278+
PyObject *ext = PyUnicode_FromString(".pyc");
279+
if (ext == NULL) {
280+
return -1;
281+
}
282+
Py_ssize_t endswith = PyUnicode_Tailmatch(filename, ext, 0, PY_SSIZE_T_MAX, +1);
283+
Py_DECREF(ext);
284+
if (endswith) {
275285
return 1;
286+
}
276287

277288
/* Only look into the file if we are allowed to close it, since
278289
it then should also be seekable. */
279-
if (closeit) {
280-
/* Read only two bytes of the magic. If the file was opened in
281-
text mode, the bytes 3 and 4 of the magic (\r\n) might not
282-
be read as they are on disk. */
283-
unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF;
284-
unsigned char buf[2];
285-
/* Mess: In case of -x, the stream is NOT at its start now,
286-
and ungetc() was used to push back the first newline,
287-
which makes the current stream position formally undefined,
288-
and a x-platform nightmare.
289-
Unfortunately, we have no direct way to know whether -x
290-
was specified. So we use a terrible hack: if the current
291-
stream position is not 0, we assume -x was specified, and
292-
give up. Bug 132850 on SourceForge spells out the
293-
hopelessness of trying anything else (fseek and ftell
294-
don't work predictably x-platform for text-mode files).
295-
*/
296-
int ispyc = 0;
297-
if (ftell(fp) == 0) {
298-
if (fread(buf, 1, 2, fp) == 2 &&
299-
((unsigned int)buf[1]<<8 | buf[0]) == halfmagic)
300-
ispyc = 1;
301-
rewind(fp);
302-
}
303-
return ispyc;
290+
if (!closeit) {
291+
return 0;
304292
}
305-
return 0;
293+
294+
/* Read only two bytes of the magic. If the file was opened in
295+
text mode, the bytes 3 and 4 of the magic (\r\n) might not
296+
be read as they are on disk. */
297+
unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF;
298+
unsigned char buf[2];
299+
/* Mess: In case of -x, the stream is NOT at its start now,
300+
and ungetc() was used to push back the first newline,
301+
which makes the current stream position formally undefined,
302+
and a x-platform nightmare.
303+
Unfortunately, we have no direct way to know whether -x
304+
was specified. So we use a terrible hack: if the current
305+
stream position is not 0, we assume -x was specified, and
306+
give up. Bug 132850 on SourceForge spells out the
307+
hopelessness of trying anything else (fseek and ftell
308+
don't work predictably x-platform for text-mode files).
309+
*/
310+
int ispyc = 0;
311+
if (ftell(fp) == 0) {
312+
if (fread(buf, 1, 2, fp) == 2 &&
313+
((unsigned int)buf[1]<<8 | buf[0]) == halfmagic)
314+
ispyc = 1;
315+
rewind(fp);
316+
}
317+
return ispyc;
306318
}
307319

320+
308321
static int
309-
set_main_loader(PyObject *d, const char *filename, const char *loader_name)
322+
set_main_loader(PyObject *d, PyObject *filename, const char *loader_name)
310323
{
311-
PyObject *filename_obj, *bootstrap, *loader_type = NULL, *loader;
312-
int result = 0;
313-
314-
filename_obj = PyUnicode_DecodeFSDefault(filename);
315-
if (filename_obj == NULL)
316-
return -1;
317324
PyInterpreterState *interp = _PyInterpreterState_GET();
318-
bootstrap = PyObject_GetAttrString(interp->importlib,
319-
"_bootstrap_external");
320-
if (bootstrap != NULL) {
321-
loader_type = PyObject_GetAttrString(bootstrap, loader_name);
322-
Py_DECREF(bootstrap);
325+
PyObject *bootstrap = PyObject_GetAttrString(interp->importlib,
326+
"_bootstrap_external");
327+
if (bootstrap == NULL) {
328+
return -1;
323329
}
330+
331+
PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name);
332+
Py_DECREF(bootstrap);
324333
if (loader_type == NULL) {
325-
Py_DECREF(filename_obj);
326334
return -1;
327335
}
328-
loader = PyObject_CallFunction(loader_type, "sN", "__main__", filename_obj);
336+
337+
PyObject *loader = PyObject_CallFunction(loader_type,
338+
"sO", "__main__", filename);
329339
Py_DECREF(loader_type);
330340
if (loader == NULL) {
331341
return -1;
332342
}
343+
333344
if (PyDict_SetItemString(d, "__loader__", loader) < 0) {
334-
result = -1;
345+
Py_DECREF(loader);
346+
return -1;
335347
}
336348
Py_DECREF(loader);
337-
return result;
349+
return 0;
338350
}
339351

340-
int
341-
PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
342-
PyCompilerFlags *flags)
352+
353+
static int
354+
pyrun_simple_file(FILE *fp, PyObject *filename, int closeit,
355+
PyCompilerFlags *flags)
343356
{
344357
PyObject *m, *d, *v;
345-
const char *ext;
346358
int set_file_name = 0, ret = -1;
347-
size_t len;
348359

349360
m = PyImport_AddModule("__main__");
350361
if (m == NULL)
@@ -355,29 +366,29 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
355366
if (PyErr_Occurred()) {
356367
goto done;
357368
}
358-
PyObject *f;
359-
f = PyUnicode_DecodeFSDefault(filename);
360-
if (f == NULL)
361-
goto done;
362-
if (PyDict_SetItemString(d, "__file__", f) < 0) {
363-
Py_DECREF(f);
369+
if (PyDict_SetItemString(d, "__file__", filename) < 0) {
364370
goto done;
365371
}
366372
if (PyDict_SetItemString(d, "__cached__", Py_None) < 0) {
367-
Py_DECREF(f);
368373
goto done;
369374
}
370375
set_file_name = 1;
371-
Py_DECREF(f);
372376
}
373-
len = strlen(filename);
374-
ext = filename + len - (len > 4 ? 4 : 0);
375-
if (maybe_pyc_file(fp, filename, ext, closeit)) {
377+
378+
int pyc = maybe_pyc_file(fp, filename, closeit);
379+
if (pyc < 0) {
380+
goto done;
381+
}
382+
383+
if (pyc) {
376384
FILE *pyc_fp;
377385
/* Try to run a pyc file. First, re-open in binary */
378-
if (closeit)
386+
if (closeit) {
379387
fclose(fp);
380-
if ((pyc_fp = _Py_fopen(filename, "rb")) == NULL) {
388+
}
389+
390+
pyc_fp = _Py_fopen_obj(filename, "rb");
391+
if (pyc_fp == NULL) {
381392
fprintf(stderr, "python: Can't reopen .pyc file\n");
382393
goto done;
383394
}
@@ -388,17 +399,17 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
388399
fclose(pyc_fp);
389400
goto done;
390401
}
391-
v = run_pyc_file(pyc_fp, filename, d, d, flags);
402+
v = run_pyc_file(pyc_fp, d, d, flags);
392403
} else {
393404
/* When running from stdin, leave __main__.__loader__ alone */
394-
if (strcmp(filename, "<stdin>") != 0 &&
405+
if (PyUnicode_CompareWithASCIIString(filename, "<stdin>") != 0 &&
395406
set_main_loader(d, filename, "SourceFileLoader") < 0) {
396407
fprintf(stderr, "python: failed to set __main__.__loader__\n");
397408
ret = -1;
398409
goto done;
399410
}
400-
v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
401-
closeit, flags);
411+
v = pyrun_file(fp, filename, Py_file_input, d, d,
412+
closeit, flags);
402413
}
403414
flush_io();
404415
if (v == NULL) {
@@ -421,6 +432,21 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
421432
return ret;
422433
}
423434

435+
436+
int
437+
PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
438+
PyCompilerFlags *flags)
439+
{
440+
PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename);
441+
if (filename_obj == NULL) {
442+
return -1;
443+
}
444+
int res = pyrun_simple_file(fp, filename_obj, closeit, flags);
445+
Py_DECREF(filename_obj);
446+
return res;
447+
}
448+
449+
424450
int
425451
PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
426452
{
@@ -1039,40 +1065,54 @@ PyRun_StringFlags(const char *str, int start, PyObject *globals,
10391065
return ret;
10401066
}
10411067

1042-
PyObject *
1043-
PyRun_FileExFlags(FILE *fp, const char *filename_str, int start, PyObject *globals,
1044-
PyObject *locals, int closeit, PyCompilerFlags *flags)
1045-
{
1046-
PyObject *ret = NULL;
1047-
mod_ty mod;
1048-
PyArena *arena = NULL;
1049-
PyObject *filename;
1050-
1051-
filename = PyUnicode_DecodeFSDefault(filename_str);
1052-
if (filename == NULL)
1053-
goto exit;
10541068

1055-
arena = PyArena_New();
1056-
if (arena == NULL)
1057-
goto exit;
1069+
static PyObject *
1070+
pyrun_file(FILE *fp, PyObject *filename, int start, PyObject *globals,
1071+
PyObject *locals, int closeit, PyCompilerFlags *flags)
1072+
{
1073+
PyArena *arena = PyArena_New();
1074+
if (arena == NULL) {
1075+
return NULL;
1076+
}
10581077

1078+
mod_ty mod;
10591079
mod = PyParser_ASTFromFileObject(fp, filename, NULL, start, NULL, NULL,
10601080
flags, NULL, arena);
10611081

1062-
if (closeit)
1082+
if (closeit) {
10631083
fclose(fp);
1064-
if (mod == NULL) {
1065-
goto exit;
10661084
}
1067-
ret = run_mod(mod, filename, globals, locals, flags, arena);
10681085

1069-
exit:
1070-
Py_XDECREF(filename);
1071-
if (arena != NULL)
1072-
PyArena_Free(arena);
1086+
PyObject *ret;
1087+
if (mod != NULL) {
1088+
ret = run_mod(mod, filename, globals, locals, flags, arena);
1089+
}
1090+
else {
1091+
ret = NULL;
1092+
}
1093+
PyArena_Free(arena);
1094+
10731095
return ret;
10741096
}
10751097

1098+
1099+
PyObject *
1100+
PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,
1101+
PyObject *locals, int closeit, PyCompilerFlags *flags)
1102+
{
1103+
PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename);
1104+
if (filename_obj == NULL) {
1105+
return NULL;
1106+
}
1107+
1108+
PyObject *res = pyrun_file(fp, filename_obj, start, globals,
1109+
locals, closeit, flags);
1110+
Py_DECREF(filename_obj);
1111+
return res;
1112+
1113+
}
1114+
1115+
10761116
static void
10771117
flush_io(void)
10781118
{
@@ -1155,8 +1195,8 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,
11551195
}
11561196

11571197
static PyObject *
1158-
run_pyc_file(FILE *fp, const char *filename, PyObject *globals,
1159-
PyObject *locals, PyCompilerFlags *flags)
1198+
run_pyc_file(FILE *fp, PyObject *globals, PyObject *locals,
1199+
PyCompilerFlags *flags)
11601200
{
11611201
PyThreadState *tstate = _PyThreadState_GET();
11621202
PyCodeObject *co;

0 commit comments

Comments
 (0)