Skip to content

Commit c5c6425

Browse files
authored
bpo-38236: Dump path config at first import error (GH-16300) (GH-16332)
Python now dumps path configuration if it fails to import the Python codecs of the filesystem and stdio encodings. (cherry picked from commit fcdb027)
1 parent fe9089a commit c5c6425

File tree

8 files changed

+129
-17
lines changed

8 files changed

+129
-17
lines changed

Doc/c-api/init_config.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,11 @@ PyConfig
425425
426426
:data:`sys.base_exec_prefix`.
427427
428+
.. c:member:: wchar_t* base_executable
429+
430+
:data:`sys._base_executable`: ``__PYVENV_LAUNCHER__`` environment
431+
variable value, or copy of :c:member:`PyConfig.executable`.
432+
428433
.. c:member:: wchar_t* base_prefix
429434
430435
:data:`sys.base_prefix`.
@@ -862,11 +867,13 @@ Path Configuration
862867
* Path configuration input fields:
863868
864869
* :c:member:`PyConfig.home`
865-
* :c:member:`PyConfig.pythonpath_env`
866870
* :c:member:`PyConfig.pathconfig_warnings`
871+
* :c:member:`PyConfig.program_name`
872+
* :c:member:`PyConfig.pythonpath_env`
867873
868874
* Path configuration output fields:
869875
876+
* :c:member:`PyConfig.base_executable`
870877
* :c:member:`PyConfig.exec_prefix`
871878
* :c:member:`PyConfig.executable`
872879
* :c:member:`PyConfig.prefix`
@@ -918,6 +925,9 @@ The following configuration files are used by the path configuration:
918925
* ``python._pth`` (Windows only)
919926
* ``pybuilddir.txt`` (Unix only)
920927
928+
The ``__PYVENV_LAUNCHER__`` environment variable is used to set
929+
:c:member:`PyConfig.base_executable`
930+
921931
922932
Py_RunMain()
923933
------------

Include/internal/pycore_pathconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ extern wchar_t* _Py_GetDLLPath(void);
6060
#endif
6161

6262
extern PyStatus _PyPathConfig_Init(void);
63+
extern void _Py_DumpPathConfig(PyThreadState *tstate);
6364

6465
#ifdef __cplusplus
6566
}

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extern int _Py_SetFileSystemEncoding(
1919
const char *encoding,
2020
const char *errors);
2121
extern void _Py_ClearFileSystemEncoding(void);
22-
extern PyStatus _PyUnicode_InitEncodings(PyInterpreterState *interp);
22+
extern PyStatus _PyUnicode_InitEncodings(PyThreadState *tstate);
2323
#ifdef MS_WINDOWS
2424
extern int _PyUnicode_EnableLegacyWindowsFSEncoding(void);
2525
#endif
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Python now dumps path configuration if it fails to import the Python codecs
2+
of the filesystem and stdio encodings.

Objects/unicodeobject.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15727,13 +15727,13 @@ config_get_codec_name(wchar_t **config_encoding)
1572715727

1572815728

1572915729
static PyStatus
15730-
init_stdio_encoding(PyInterpreterState *interp)
15730+
init_stdio_encoding(PyThreadState *tstate)
1573115731
{
1573215732
/* Update the stdio encoding to the normalized Python codec name. */
15733-
PyConfig *config = &interp->config;
15733+
PyConfig *config = &tstate->interp->config;
1573415734
if (config_get_codec_name(&config->stdio_encoding) < 0) {
1573515735
return _PyStatus_ERR("failed to get the Python codec name "
15736-
"of the stdio encoding");
15736+
"of the stdio encoding");
1573715737
}
1573815738
return _PyStatus_OK();
1573915739
}
@@ -15787,15 +15787,18 @@ init_fs_codec(PyInterpreterState *interp)
1578715787

1578815788

1578915789
static PyStatus
15790-
init_fs_encoding(PyInterpreterState *interp)
15790+
init_fs_encoding(PyThreadState *tstate)
1579115791
{
15792+
PyInterpreterState *interp = tstate->interp;
15793+
1579215794
/* Update the filesystem encoding to the normalized Python codec name.
1579315795
For example, replace "ANSI_X3.4-1968" (locale encoding) with "ascii"
1579415796
(Python codec name). */
1579515797
PyConfig *config = &interp->config;
1579615798
if (config_get_codec_name(&config->filesystem_encoding) < 0) {
15799+
_Py_DumpPathConfig(tstate);
1579715800
return _PyStatus_ERR("failed to get the Python codec "
15798-
"of the filesystem encoding");
15801+
"of the filesystem encoding");
1579915802
}
1580015803

1580115804
if (init_fs_codec(interp) < 0) {
@@ -15806,14 +15809,14 @@ init_fs_encoding(PyInterpreterState *interp)
1580615809

1580715810

1580815811
PyStatus
15809-
_PyUnicode_InitEncodings(PyInterpreterState *interp)
15812+
_PyUnicode_InitEncodings(PyThreadState *tstate)
1581015813
{
15811-
PyStatus status = init_fs_encoding(interp);
15814+
PyStatus status = init_fs_encoding(tstate);
1581215815
if (_PyStatus_EXCEPTION(status)) {
1581315816
return status;
1581415817
}
1581515818

15816-
return init_stdio_encoding(interp);
15819+
return init_stdio_encoding(tstate);
1581715820
}
1581815821

1581915822

PC/getpathp.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,7 @@ _Py_GetDLLPath(void)
532532

533533

534534
static PyStatus
535-
get_program_full_path(const PyConfig *config,
536-
PyCalculatePath *calculate, _PyPathConfig *pathconfig)
535+
get_program_full_path(_PyPathConfig *pathconfig)
537536
{
538537
const wchar_t *pyvenv_launcher;
539538
wchar_t program_full_path[MAXPATHLEN+1];
@@ -977,7 +976,7 @@ calculate_path_impl(const PyConfig *config,
977976
{
978977
PyStatus status;
979978

980-
status = get_program_full_path(config, calculate, pathconfig);
979+
status = get_program_full_path(pathconfig);
981980
if (_PyStatus_EXCEPTION(status)) {
982981
return status;
983982
}

Python/initconfig.c

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
#include "Python.h"
22
#include "osdefs.h" /* DELIM */
3-
#include "pycore_initconfig.h"
43
#include "pycore_fileutils.h"
54
#include "pycore_getopt.h"
5+
#include "pycore_initconfig.h"
6+
#include "pycore_pathconfig.h"
7+
#include "pycore_pyerrors.h"
68
#include "pycore_pylifecycle.h"
79
#include "pycore_pymem.h"
810
#include "pycore_pystate.h" /* _PyRuntime */
9-
#include "pycore_pathconfig.h"
1011
#include <locale.h> /* setlocale() */
1112
#ifdef HAVE_LANGINFO_H
1213
# include <langinfo.h> /* nl_langinfo(CODESET) */
@@ -2492,3 +2493,98 @@ _Py_GetConfigsAsDict(void)
24922493
Py_XDECREF(dict);
24932494
return NULL;
24942495
}
2496+
2497+
2498+
static void
2499+
init_dump_ascii_wstr(const wchar_t *str)
2500+
{
2501+
if (str == NULL) {
2502+
PySys_WriteStderr("(not set)");
2503+
return;
2504+
}
2505+
2506+
PySys_WriteStderr("'");
2507+
for (; *str != L'\0'; str++) {
2508+
wchar_t ch = *str;
2509+
if (ch == L'\'') {
2510+
PySys_WriteStderr("\\'");
2511+
} else if (0x20 <= ch && ch < 0x7f) {
2512+
PySys_WriteStderr("%lc", ch);
2513+
}
2514+
else if (ch <= 0xff) {
2515+
PySys_WriteStderr("\\x%02x", ch);
2516+
}
2517+
#if SIZEOF_WCHAR_T > 2
2518+
else if (ch > 0xffff) {
2519+
PySys_WriteStderr("\\U%08x", ch);
2520+
}
2521+
#endif
2522+
else {
2523+
PySys_WriteStderr("\\u%04x", ch);
2524+
}
2525+
}
2526+
PySys_WriteStderr("'");
2527+
}
2528+
2529+
2530+
/* Dump the Python path configuration into sys.stderr */
2531+
void
2532+
_Py_DumpPathConfig(PyThreadState *tstate)
2533+
{
2534+
PyObject *exc_type, *exc_value, *exc_tb;
2535+
_PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
2536+
2537+
PySys_WriteStderr("Python path configuration:\n");
2538+
2539+
#define DUMP_CONFIG(NAME, FIELD) \
2540+
do { \
2541+
PySys_WriteStderr(" " NAME " = "); \
2542+
init_dump_ascii_wstr(config->FIELD); \
2543+
PySys_WriteStderr("\n"); \
2544+
} while (0)
2545+
2546+
PyConfig *config = &tstate->interp->config;
2547+
DUMP_CONFIG("PYTHONHOME", home);
2548+
DUMP_CONFIG("PYTHONPATH", pythonpath_env);
2549+
DUMP_CONFIG("program name", program_name);
2550+
PySys_WriteStderr(" isolated = %i\n", config->isolated);
2551+
PySys_WriteStderr(" environment = %i\n", config->use_environment);
2552+
PySys_WriteStderr(" user site = %i\n", config->user_site_directory);
2553+
PySys_WriteStderr(" import site = %i\n", config->site_import);
2554+
#undef DUMP_CONFIG
2555+
2556+
#define DUMP_SYS(NAME) \
2557+
do { \
2558+
obj = PySys_GetObject(#NAME); \
2559+
PySys_FormatStderr(" sys.%s = ", #NAME); \
2560+
if (obj != NULL) { \
2561+
PySys_FormatStderr("%A", obj); \
2562+
} \
2563+
else { \
2564+
PySys_WriteStderr("(not set)"); \
2565+
} \
2566+
PySys_FormatStderr("\n"); \
2567+
} while (0)
2568+
2569+
PyObject *obj;
2570+
DUMP_SYS(_base_executable);
2571+
DUMP_SYS(base_prefix);
2572+
DUMP_SYS(base_exec_prefix);
2573+
DUMP_SYS(executable);
2574+
DUMP_SYS(prefix);
2575+
DUMP_SYS(exec_prefix);
2576+
#undef DUMP_SYS
2577+
2578+
PyObject *sys_path = PySys_GetObject("path"); /* borrowed reference */
2579+
if (sys_path != NULL && PyList_Check(sys_path)) {
2580+
PySys_WriteStderr(" sys.path = [\n");
2581+
Py_ssize_t len = PyList_GET_SIZE(sys_path);
2582+
for (Py_ssize_t i=0; i < len; i++) {
2583+
PyObject *path = PyList_GET_ITEM(sys_path, i);
2584+
PySys_FormatStderr(" %A,\n", path);
2585+
}
2586+
PySys_WriteStderr(" ]\n");
2587+
}
2588+
2589+
_PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
2590+
}

Python/pylifecycle.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,8 @@ pyinit_main(_PyRuntimeState *runtime, PyInterpreterState *interp)
941941
return status;
942942
}
943943

944-
status = _PyUnicode_InitEncodings(interp);
944+
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
945+
status = _PyUnicode_InitEncodings(tstate);
945946
if (_PyStatus_EXCEPTION(status)) {
946947
return status;
947948
}
@@ -1508,7 +1509,7 @@ new_interpreter(PyThreadState **tstate_p)
15081509
return status;
15091510
}
15101511

1511-
status = _PyUnicode_InitEncodings(interp);
1512+
status = _PyUnicode_InitEncodings(tstate);
15121513
if (_PyStatus_EXCEPTION(status)) {
15131514
return status;
15141515
}

0 commit comments

Comments
 (0)