Skip to content

Commit e9b428f

Browse files
committed
Reimplement addbuilddir() in C inside getpath.c, so as to execute it
at interpreter startup before importing any non-builtin modules. Should fix #9589.
1 parent 09c449c commit e9b428f

File tree

8 files changed

+53
-25
lines changed

8 files changed

+53
-25
lines changed

.bzrignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ python
1111
build
1212
Makefile.pre
1313
platform
14+
pybuilddir.txt
1415
pyconfig.h
1516
libpython*.a
1617
python.exe

.hgignore

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Modules/config.c
3232
Parser/pgen$
3333
^core
3434
^python-gdb.py
35+
^pybuilddir.txt
3536

3637
syntax: glob
3738
libpython*.a

Include/Python.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ extern "C" {
129129
/* _Py_Mangle is defined in compile.c */
130130
PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
131131

132-
/* _Py_char2wchar lives in main.c */
132+
/* These functions live in main.c */
133133
PyAPI_FUNC(wchar_t *) _Py_char2wchar(char *);
134+
PyAPI_FUNC(FILE *) _Py_wfopen(const wchar_t *path, const wchar_t *mode);
134135
#ifdef __cplusplus
135136
}
136137
#endif

Lib/site.py

-15
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,6 @@ def removeduppaths():
107107
sys.path[:] = L
108108
return known_paths
109109

110-
# XXX This should not be part of site.py, since it is needed even when
111-
# using the -S option for Python. See http://www.python.org/sf/586680
112-
def addbuilddir():
113-
"""Append ./build/lib.<platform> in case we're running in the build dir
114-
(especially for Guido :-)"""
115-
from sysconfig import get_platform
116-
s = "build/lib.%s-%.3s" % (get_platform(), sys.version)
117-
if hasattr(sys, 'gettotalrefcount'):
118-
s += '-pydebug'
119-
s = os.path.join(os.path.dirname(sys.path.pop()), s)
120-
sys.path.append(s)
121-
122110

123111
def _init_pathinfo():
124112
"""Return a set containing all existing directory entries from sys.path"""
@@ -529,9 +517,6 @@ def main():
529517

530518
abs_paths()
531519
known_paths = removeduppaths()
532-
if (os.name == "posix" and sys.path and
533-
os.path.basename(sys.path[-1]) == "Modules"):
534-
addbuilddir()
535520
if ENABLE_USER_SITE is None:
536521
ENABLE_USER_SITE = check_enableusersite()
537522
known_paths = addusersitepackages(known_paths)

Makefile.pre.in

+1
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,7 @@ distclean: clobber
12131213
Modules/Setup Modules/Setup.local Modules/Setup.config \
12141214
Misc/python.pc
12151215
-rm -f python*-gdb.py
1216+
-rm -f pybuilddir.txt
12161217
find $(srcdir) '(' -name '*.fdc' -o -name '*~' \
12171218
-o -name '[@,#]*' -o -name '*.old' \
12181219
-o -name '*.orig' -o -name '*.rej' \

Modules/getpath.c

+27-4
Original file line numberDiff line numberDiff line change
@@ -394,12 +394,35 @@ search_for_exec_prefix(wchar_t *argv0_path, wchar_t *home)
394394
return 1;
395395
}
396396

397-
/* Check to see if argv[0] is in the build directory */
397+
/* Check to see if argv[0] is in the build directory. "pybuilddir.txt"
398+
is written by setup.py and contains the relative path to the location
399+
of shared library modules. */
398400
wcscpy(exec_prefix, argv0_path);
399-
joinpath(exec_prefix, L"Modules/Setup");
401+
joinpath(exec_prefix, L"pybuilddir.txt");
400402
if (isfile(exec_prefix)) {
401-
reduce(exec_prefix);
402-
return -1;
403+
FILE *f = _Py_wfopen(exec_prefix, L"rb");
404+
if (f == NULL)
405+
errno = 0;
406+
else {
407+
char buf[MAXPATHLEN+1];
408+
PyObject *decoded;
409+
wchar_t rel_builddir_path[MAXPATHLEN+1];
410+
size_t n;
411+
n = fread(buf, 1, MAXPATHLEN, f);
412+
buf[n] = '\0';
413+
fclose(f);
414+
decoded = PyUnicode_DecodeUTF8(buf, n, "surrogateescape");
415+
if (decoded != NULL) {
416+
n = PyUnicode_AsWideChar(decoded, rel_builddir_path, MAXPATHLEN);
417+
Py_DECREF(decoded);
418+
if (n >= 0) {
419+
rel_builddir_path[n] = L'\0';
420+
wcscpy(exec_prefix, argv0_path);
421+
joinpath(exec_prefix, rel_builddir_path);
422+
return -1;
423+
}
424+
}
425+
}
403426
}
404427

405428
/* Search from argv0_path, until root is found */

Modules/main.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ PYTHONCASEOK : ignore case in 'import' statements (Windows).\n\
101101
PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n\
102102
";
103103

104-
#ifndef MS_WINDOWS
105-
static FILE*
106-
_wfopen(const wchar_t *path, const wchar_t *mode)
104+
FILE *
105+
_Py_wfopen(const wchar_t *path, const wchar_t *mode)
107106
{
107+
#ifndef MS_WINDOWS
108108
char cpath[PATH_MAX];
109109
char cmode[10];
110110
size_t r;
@@ -119,8 +119,10 @@ _wfopen(const wchar_t *path, const wchar_t *mode)
119119
return NULL;
120120
}
121121
return fopen(cpath, cmode);
122-
}
122+
#else
123+
return _wfopen(path, mode);
123124
#endif
125+
}
124126

125127

126128
static int
@@ -640,7 +642,7 @@ Py_Main(int argc, wchar_t **argv)
640642
}
641643

642644
if (sts==-1 && filename!=NULL) {
643-
if ((fp = _wfopen(filename, L"r")) == NULL) {
645+
if ((fp = _Py_wfopen(filename, L"r")) == NULL) {
644646
char cfilename[PATH_MAX];
645647
size_t r = wcstombs(cfilename, filename, PATH_MAX);
646648
if (r == PATH_MAX)

setup.py

+14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
# This global variable is used to hold the list of modules to be disabled.
2323
disabled_module_list = []
2424

25+
# File which contains the directory for shared mods (for sys.path fixup
26+
# when running from the build dir, see Modules/getpath.c)
27+
_BUILDDIR_COOKIE = "pybuilddir.txt"
28+
2529
def add_dir_to_list(dirlist, dir):
2630
"""Add the directory 'dir' to the list 'dirlist' (at the front) if
2731
1) 'dir' is not already in 'dirlist'
@@ -224,6 +228,16 @@ def build_extensions(self):
224228
args['compiler_so'] = compiler + ' ' + ccshared + ' ' + cflags
225229
self.compiler.set_executables(**args)
226230

231+
# Not only do we write the builddir cookie, but we manually install
232+
# the shared modules directory if it isn't already in sys.path.
233+
# Otherwise trying to import the extensions after building them
234+
# will fail.
235+
with open(_BUILDDIR_COOKIE, "wb") as f:
236+
f.write(self.build_lib.encode('utf-8', 'surrogateescape'))
237+
abs_build_lib = os.path.join(os.path.dirname(__file__), self.build_lib)
238+
if abs_build_lib not in sys.path:
239+
sys.path.append(abs_build_lib)
240+
227241
build_ext.build_extensions(self)
228242

229243
longest = max([len(e.name) for e in self.extensions])

0 commit comments

Comments
 (0)