[Python-checkins] bpo-38236: Dump path config at first import error (GH-16300)

Victor Stinner webhook-mailer at python.org
Mon Sep 23 08:46:04 EDT 2019


https://github.com/python/cpython/commit/fcdb027234566c4d506d6d753c7d5638490fb088
commit: fcdb027234566c4d506d6d753c7d5638490fb088
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-09-23T14:45:47+02:00
summary:

bpo-38236: Dump path config at first import error (GH-16300)

Python now dumps path configuration if it fails to import the Python
codecs of the filesystem and stdio encodings.

files:
A Misc/NEWS.d/next/Core and Builtins/2019-09-20-19-06-23.bpo-38236.eQ0Tmj.rst
M Doc/c-api/init_config.rst
M Include/internal/pycore_pathconfig.h
M Objects/unicodeobject.c
M PC/getpathp.c
M Python/initconfig.c

diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst
index c198df3fb1d0..bc24fa081317 100644
--- a/Doc/c-api/init_config.rst
+++ b/Doc/c-api/init_config.rst
@@ -425,6 +425,11 @@ PyConfig
 
       :data:`sys.base_exec_prefix`.
 
+   .. c:member:: wchar_t* base_executable
+
+      :data:`sys._base_executable`: ``__PYVENV_LAUNCHER__`` environment
+      variable value, or copy of :c:member:`PyConfig.executable`.
+
    .. c:member:: wchar_t* base_prefix
 
       :data:`sys.base_prefix`.
@@ -862,11 +867,13 @@ Path Configuration
 * Path configuration input fields:
 
   * :c:member:`PyConfig.home`
-  * :c:member:`PyConfig.pythonpath_env`
   * :c:member:`PyConfig.pathconfig_warnings`
+  * :c:member:`PyConfig.program_name`
+  * :c:member:`PyConfig.pythonpath_env`
 
 * Path configuration output fields:
 
+  * :c:member:`PyConfig.base_executable`
   * :c:member:`PyConfig.exec_prefix`
   * :c:member:`PyConfig.executable`
   * :c:member:`PyConfig.prefix`
@@ -918,6 +925,9 @@ The following configuration files are used by the path configuration:
 * ``python._pth`` (Windows only)
 * ``pybuilddir.txt`` (Unix only)
 
+The ``__PYVENV_LAUNCHER__`` environment variable is used to set
+:c:member:`PyConfig.base_executable`
+
 
 Py_RunMain()
 ------------
diff --git a/Include/internal/pycore_pathconfig.h b/Include/internal/pycore_pathconfig.h
index a2c488cdc2eb..61b3790fe1f4 100644
--- a/Include/internal/pycore_pathconfig.h
+++ b/Include/internal/pycore_pathconfig.h
@@ -60,6 +60,7 @@ extern wchar_t* _Py_GetDLLPath(void);
 #endif
 
 extern PyStatus _PyPathConfig_Init(void);
+extern void _Py_DumpPathConfig(PyThreadState *tstate);
 
 #ifdef __cplusplus
 }
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-09-20-19-06-23.bpo-38236.eQ0Tmj.rst b/Misc/NEWS.d/next/Core and Builtins/2019-09-20-19-06-23.bpo-38236.eQ0Tmj.rst
new file mode 100644
index 000000000000..5c7729296cee
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-09-20-19-06-23.bpo-38236.eQ0Tmj.rst	
@@ -0,0 +1,2 @@
+Python now dumps path configuration if it fails to import the Python codecs
+of the filesystem and stdio encodings.
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 070f795aa3cb..55b251918173 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -15804,13 +15804,13 @@ config_get_codec_name(wchar_t **config_encoding)
 
 
 static PyStatus
-init_stdio_encoding(PyInterpreterState *interp)
+init_stdio_encoding(PyThreadState *tstate)
 {
     /* Update the stdio encoding to the normalized Python codec name. */
-    PyConfig *config = &interp->config;
+    PyConfig *config = &tstate->interp->config;
     if (config_get_codec_name(&config->stdio_encoding) < 0) {
         return _PyStatus_ERR("failed to get the Python codec name "
-                            "of the stdio encoding");
+                             "of the stdio encoding");
     }
     return _PyStatus_OK();
 }
@@ -15864,15 +15864,18 @@ init_fs_codec(PyInterpreterState *interp)
 
 
 static PyStatus
-init_fs_encoding(PyInterpreterState *interp)
+init_fs_encoding(PyThreadState *tstate)
 {
+    PyInterpreterState *interp = tstate->interp;
+
     /* Update the filesystem encoding to the normalized Python codec name.
        For example, replace "ANSI_X3.4-1968" (locale encoding) with "ascii"
        (Python codec name). */
     PyConfig *config = &interp->config;
     if (config_get_codec_name(&config->filesystem_encoding) < 0) {
+        _Py_DumpPathConfig(tstate);
         return _PyStatus_ERR("failed to get the Python codec "
-                            "of the filesystem encoding");
+                             "of the filesystem encoding");
     }
 
     if (init_fs_codec(interp) < 0) {
@@ -15885,14 +15888,12 @@ init_fs_encoding(PyInterpreterState *interp)
 PyStatus
 _PyUnicode_InitEncodings(PyThreadState *tstate)
 {
-    PyInterpreterState *interp = tstate->interp;
-
-    PyStatus status = init_fs_encoding(interp);
+    PyStatus status = init_fs_encoding(tstate);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
-    return init_stdio_encoding(interp);
+    return init_stdio_encoding(tstate);
 }
 
 
diff --git a/PC/getpathp.c b/PC/getpathp.c
index 0ee53080bf31..888bef1146fd 100644
--- a/PC/getpathp.c
+++ b/PC/getpathp.c
@@ -532,8 +532,7 @@ _Py_GetDLLPath(void)
 
 
 static PyStatus
-get_program_full_path(const PyConfig *config,
-                      PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+get_program_full_path(_PyPathConfig *pathconfig)
 {
     const wchar_t *pyvenv_launcher;
     wchar_t program_full_path[MAXPATHLEN+1];
@@ -977,7 +976,7 @@ calculate_path_impl(const PyConfig *config,
 {
     PyStatus status;
 
-    status = get_program_full_path(config, calculate, pathconfig);
+    status = get_program_full_path(pathconfig);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
diff --git a/Python/initconfig.c b/Python/initconfig.c
index e67fea7b109c..cda4c863736c 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -1,12 +1,13 @@
 #include "Python.h"
 #include "osdefs.h"       /* DELIM */
-#include "pycore_initconfig.h"
 #include "pycore_fileutils.h"
 #include "pycore_getopt.h"
+#include "pycore_initconfig.h"
+#include "pycore_pathconfig.h"
+#include "pycore_pyerrors.h"
 #include "pycore_pylifecycle.h"
 #include "pycore_pymem.h"
 #include "pycore_pystate.h"   /* _PyRuntime */
-#include "pycore_pathconfig.h"
 #include <locale.h>       /* setlocale() */
 #ifdef HAVE_LANGINFO_H
 #  include <langinfo.h>   /* nl_langinfo(CODESET) */
@@ -2539,3 +2540,97 @@ _Py_GetConfigsAsDict(void)
     Py_XDECREF(dict);
     return NULL;
 }
+
+
+static void
+init_dump_ascii_wstr(const wchar_t *str)
+{
+    if (str == NULL) {
+        PySys_WriteStderr("(not set)");
+    }
+
+    PySys_WriteStderr("'");
+    for (; *str != L'\0'; str++) {
+        wchar_t ch = *str;
+        if (ch == L'\'') {
+            PySys_WriteStderr("\\'");
+        } else if (0x20 <= ch && ch < 0x7f) {
+            PySys_WriteStderr("%lc", ch);
+        }
+        else if (ch <= 0xff) {
+            PySys_WriteStderr("\\x%02x", ch);
+        }
+#if SIZEOF_WCHAR_T > 2
+        else if (ch > 0xffff) {
+            PySys_WriteStderr("\\U%08x", ch);
+        }
+#endif
+        else {
+            PySys_WriteStderr("\\u%04x", ch);
+        }
+    }
+    PySys_WriteStderr("'");
+}
+
+
+/* Dump the Python path configuration into sys.stderr */
+void
+_Py_DumpPathConfig(PyThreadState *tstate)
+{
+    PyObject *exc_type, *exc_value, *exc_tb;
+    _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
+
+    PySys_WriteStderr("Python path configuration:\n");
+
+#define DUMP_CONFIG(NAME, FIELD) \
+        do { \
+            PySys_WriteStderr("  " NAME " = "); \
+            init_dump_ascii_wstr(config->FIELD); \
+            PySys_WriteStderr("\n"); \
+        } while (0)
+
+    PyConfig *config = &tstate->interp->config;
+    DUMP_CONFIG("PYTHONHOME", home);
+    DUMP_CONFIG("PYTHONPATH", pythonpath_env);
+    DUMP_CONFIG("program name", program_name);
+    PySys_WriteStderr("  isolated = %i\n", config->isolated);
+    PySys_WriteStderr("  environment = %i\n", config->use_environment);
+    PySys_WriteStderr("  user site = %i\n", config->user_site_directory);
+    PySys_WriteStderr("  import site = %i\n", config->site_import);
+#undef DUMP_CONFIG
+
+#define DUMP_SYS(NAME) \
+        do { \
+            obj = PySys_GetObject(#NAME); \
+            PySys_FormatStderr("  sys.%s = ", #NAME); \
+            if (obj != NULL) { \
+                PySys_FormatStderr("%A", obj); \
+            } \
+            else { \
+                PySys_WriteStderr("(not set)"); \
+            } \
+            PySys_FormatStderr("\n"); \
+        } while (0)
+
+    PyObject *obj;
+    DUMP_SYS(_base_executable);
+    DUMP_SYS(base_prefix);
+    DUMP_SYS(base_exec_prefix);
+    DUMP_SYS(executable);
+    DUMP_SYS(prefix);
+    DUMP_SYS(exec_prefix);
+#undef DUMP_SYS
+
+    PyObject *sys_path = PySys_GetObject("path");  /* borrowed reference */
+    if (sys_path != NULL && PyList_Check(sys_path)) {
+        PySys_WriteStderr("  sys.path = [\n");
+        Py_ssize_t len = PyList_GET_SIZE(sys_path);
+        for (Py_ssize_t i=0; i < len; i++) {
+            PyObject *path = PyList_GET_ITEM(sys_path, i);
+            PySys_FormatStderr("    %A,\n", path);
+        }
+        PySys_WriteStderr("  ]\n");
+    }
+
+    _PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
+}



More information about the Python-checkins mailing list