Преглед изворни кода

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.
rdb пре 8 година
родитељ
комит
da2ad0f0bc
2 измењених фајлова са 68 додато и 0 уклоњено
  1. 2 0
      direct/src/showutil/FreezeTool.py
  2. 66 0
      pandatool/src/deploy-stub/deploy-stub.c

+ 2 - 0
direct/src/showutil/FreezeTool.py

@@ -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 - 0
pandatool/src/deploy-stub/deploy-stub.c

@@ -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());