deploy-ng: default to ACP if console codepage codec wasn't frozen

This is necessary because when Python is initialized, it takes the codec to use from GetConsoleCP() and GetConsoleOutputCP() without bothering to check whether the given codec is available.  However, in most cases, the console codepage will be the same as the ANSI codepage (ie. GetACP()) which is always supported by Python via the 'mbcs' codec.

So what we do is we check whether the console codepage is frozen in, and if not, we set the console codepage to the ANSI codepage and set the stdin/stdout/stderr encoding to 'mbcs'.

This is still not a perfect solution because the ACP may not be able to encode all characters that the application is printing, which would still result in unexpected errors.  Ideally, we'd pull in Python 3.6's _io._WindowsConsoleIO class, which bypasses this whole mess by directly using the wide-character Windows APIs to write to the console.
This commit is contained in:
rdb
2017-11-22 19:33:17 +01:00
parent 6ec4e5db68
commit da2ad0f0bc
2 changed files with 68 additions and 0 deletions
+2
View File
@@ -30,6 +30,8 @@ isDebugBuild = (python.lower().endswith('_d'))
# These are modules that Python always tries to import up-front. They
# must be frozen in any main.exe.
# NB. if encodings are removed, be sure to remove them from the shortcut in
# deploy-stub.c.
startupModules = [
'encodings', 'encodings.aliases', 'encodings.undefined', 'encodings.ascii',
'encodings.cp1252', 'encodings.latin_1', 'encodings.utf_8',
+66
View File
@@ -38,6 +38,32 @@ static struct _inittab extensions[] = {
#endif
#endif
#if defined(_WIN32) && PY_VERSION_HEX < 0x03060000
static int supports_code_page(UINT cp) {
/* Shortcut, because we know that these encodings are bundled by default--
* see FreezeTool.py and Python's encodings/aliases.py */
if (cp != 0 && cp != 1252 && cp != 367 && cp != 437 && cp != 850 && cp != 819) {
const struct _frozen *moddef;
char codec[100];
/* Check if the codec was frozen into the program. We can't check this
* using _PyCodec_Lookup, since Python hasn't been initialized yet. */
PyOS_snprintf(codec, sizeof(codec), "encodings.cp%u", (unsigned int)cp);
moddef = PyImport_FrozenModules;
while (moddef->name) {
if (strcmp(moddef->name, codec) == 0) {
return 1;
}
++moddef;
}
return 0;
}
return 1;
}
#endif
/* Main program */
#ifdef WIN_UNICODE
@@ -64,6 +90,19 @@ int Py_FrozenMain(int argc, char **argv)
}
#endif
#if defined(MS_WINDOWS) && PY_VERSION_HEX >= 0x03040000 && PY_VERSION_HEX < 0x03060000
if (!supports_code_page(GetConsoleOutputCP()) ||
!supports_code_page(GetConsoleCP())) {
/* Revert to the active codepage, and tell Python to use the 'mbcs'
* encoding (which always uses the active codepage). In 99% of cases,
* this will be the same thing anyway. */
UINT acp = GetACP();
SetConsoleCP(acp);
SetConsoleOutputCP(acp);
Py_SetStandardStreamEncoding("mbcs", NULL);
}
#endif
Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
Py_NoSiteFlag = 1;
Py_NoUserSiteDirectory = 1;
@@ -112,6 +151,33 @@ int Py_FrozenMain(int argc, char **argv)
PyWinFreeze_ExeInit();
#endif
#if defined(MS_WINDOWS) && PY_VERSION_HEX < 0x03040000
if (!supports_code_page(GetConsoleOutputCP()) ||
!supports_code_page(GetConsoleCP())) {
/* Same hack as before except for Python 2.7, which doesn't seem to have
* a way to set the encoding ahead of time, and setting PYTHONIOENCODING
* doesn't seem to work. Fortunately, Python 2.7 doesn't usually start
* causing codec errors until the first print statement. */
PyObject *sys_stream;
UINT acp = GetACP();
SetConsoleCP(acp);
SetConsoleOutputCP(acp);
sys_stream = PySys_GetObject("stdin");
if (sys_stream && PyFile_Check(sys_stream)) {
PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL);
}
sys_stream = PySys_GetObject("stdout");
if (sys_stream && PyFile_Check(sys_stream)) {
PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL);
}
sys_stream = PySys_GetObject("stderr");
if (sys_stream && PyFile_Check(sys_stream)) {
PyFile_SetEncodingAndErrors(sys_stream, "mbcs", NULL);
}
}
#endif
if (Py_VerboseFlag)
fprintf(stderr, "Python %s\n%s\n",
Py_GetVersion(), Py_GetCopyright());