Issue #23955: Add pyvenv.cfg option to suppress registry/environment lookup for generating sys.path.
Also cleans up and secures getpathp.c
This commit is contained in:
parent
d9ef74e3dd
commit
4a7fe7e397
|
@ -661,6 +661,17 @@ This is how :data:`sys.path` is populated on Windows:
|
|||
the environment, and no registry entries can be found, a default path with
|
||||
relative entries is used (e.g. ``.\Lib;.\plat-win``, etc).
|
||||
|
||||
If a ``pyvenv.cfg`` file is found alongside the main executable or in the
|
||||
directory one level above the executable, the following variations apply:
|
||||
|
||||
* If ``home`` is an absolute path and :envvar:`PYTHONHOME` is not set, this
|
||||
path is used instead of the path to the main executable when deducing the
|
||||
home location.
|
||||
|
||||
* If ``applocal`` is set to true, the ``home`` property or the main executable
|
||||
is always used as the home path, and all environment variables or registry
|
||||
values affecting the path are ignored. The landmark file is not checked.
|
||||
|
||||
The end result of all this is:
|
||||
|
||||
* When running :file:`python.exe`, or any other .exe in the main Python
|
||||
|
@ -672,13 +683,17 @@ The end result of all this is:
|
|||
etc), the "Python Home" will not be deduced, so the core path from the
|
||||
registry is used. Other "application paths" in the registry are always read.
|
||||
|
||||
* If Python can't find its home and there is no registry (eg, frozen .exe, some
|
||||
very strange installation setup) you get a path with some default, but
|
||||
* If Python can't find its home and there are no registry value (frozen .exe,
|
||||
some very strange installation setup) you get a path with some default, but
|
||||
relative, paths.
|
||||
|
||||
For those who want to bundle Python into their application or distribution, the
|
||||
following advice will prevent conflicts with other installations:
|
||||
|
||||
* Include a ``pyvenv.cfg`` file alongside your executable containing
|
||||
``applocal = true``. This will ensure that your own directory will be used to
|
||||
resolve paths even if you have included the standard library in a ZIP file.
|
||||
|
||||
* If you are loading :file:`python3.dll` or :file:`python35.dll` in your own
|
||||
executable, explicitly call :c:func:`Py_SetPath` or (at least)
|
||||
:c:func:`Py_SetProgramName` before :c:func:`Py_Initialize`.
|
||||
|
@ -688,7 +703,7 @@ following advice will prevent conflicts with other installations:
|
|||
|
||||
* If you cannot use the previous suggestions (for example, you are a
|
||||
distribution that allows people to run :file:`python.exe` directly), ensure
|
||||
that the landmark file (:file:`Lib\\os.py`) exists in your bundled library.
|
||||
that the landmark file (:file:`Lib\\os.py`) exists in your install directory.
|
||||
(Note that it will not be detected inside a ZIP file.)
|
||||
|
||||
These will ensure that the files in a system-wide installation will not take
|
||||
|
|
|
@ -10,6 +10,9 @@ Release date: 2015-05-24
|
|||
Core and Builtins
|
||||
-----------------
|
||||
|
||||
- Issue #23955: Add pyvenv.cfg option to suppress registry/environment
|
||||
lookup for generating sys.path on Windows.
|
||||
|
||||
- Issue #24257: Fixed system error in the comparison of faked
|
||||
types.SimpleNamespace.
|
||||
|
||||
|
|
178
PC/getpathp.c
178
PC/getpathp.c
|
@ -113,7 +113,10 @@ is_sep(wchar_t ch) /* determine if "ch" is a separator character */
|
|||
static void
|
||||
reduce(wchar_t *dir)
|
||||
{
|
||||
size_t i = wcslen(dir);
|
||||
size_t i = wcsnlen_s(dir, MAXPATHLEN+1);
|
||||
if (i >= MAXPATHLEN+1)
|
||||
Py_FatalError("buffer overflow in getpathp.c's reduce()");
|
||||
|
||||
while (i > 0 && !is_sep(dir[i]))
|
||||
--i;
|
||||
dir[i] = '\0';
|
||||
|
@ -130,16 +133,23 @@ exists(wchar_t *filename)
|
|||
may extend 'filename' by one character.
|
||||
*/
|
||||
static int
|
||||
ismodule(wchar_t *filename) /* Is module -- check for .pyc/.pyo too */
|
||||
ismodule(wchar_t *filename, int update_filename) /* Is module -- check for .pyc/.pyo too */
|
||||
{
|
||||
int n;
|
||||
|
||||
if (exists(filename))
|
||||
return 1;
|
||||
|
||||
/* Check for the compiled version of prefix. */
|
||||
if (wcslen(filename) < MAXPATHLEN) {
|
||||
wcscat(filename, Py_OptimizeFlag ? L"o" : L"c");
|
||||
if (exists(filename))
|
||||
return 1;
|
||||
n = wcsnlen_s(filename, MAXPATHLEN+1);
|
||||
if (n < MAXPATHLEN) {
|
||||
int exist = 0;
|
||||
filename[n] = Py_OptimizeFlag ? L'o' : L'c';
|
||||
filename[n + 1] = L'\0';
|
||||
exist = exists(filename);
|
||||
if (!update_filename)
|
||||
filename[n] = L'\0';
|
||||
return exist;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -154,23 +164,23 @@ ismodule(wchar_t *filename) /* Is module -- check for .pyc/.pyo too */
|
|||
stuff as fits will be appended.
|
||||
*/
|
||||
static void
|
||||
join(wchar_t *buffer, wchar_t *stuff)
|
||||
join(wchar_t *buffer, const wchar_t *stuff)
|
||||
{
|
||||
size_t n, k;
|
||||
if (is_sep(stuff[0]))
|
||||
n = 0;
|
||||
else {
|
||||
n = wcslen(buffer);
|
||||
if (n > 0 && !is_sep(buffer[n-1]) && n < MAXPATHLEN)
|
||||
buffer[n++] = SEP;
|
||||
size_t n;
|
||||
if (is_sep(stuff[0]) ||
|
||||
(wcsnlen_s(stuff, 4) >= 3 && stuff[1] == ':' && is_sep(stuff[2]))) {
|
||||
if (wcscpy_s(buffer, MAXPATHLEN+1, stuff) != 0)
|
||||
Py_FatalError("buffer overflow in getpathp.c's join()");
|
||||
return;
|
||||
}
|
||||
if (n > MAXPATHLEN)
|
||||
Py_FatalError("buffer overflow in getpathp.c's joinpath()");
|
||||
k = wcslen(stuff);
|
||||
if (n + k > MAXPATHLEN)
|
||||
k = MAXPATHLEN - n;
|
||||
wcsncpy(buffer+n, stuff, k);
|
||||
buffer[n+k] = '\0';
|
||||
|
||||
n = wcsnlen_s(buffer, MAXPATHLEN+1);
|
||||
if (n > 0 && !is_sep(buffer[n - 1]) && n < MAXPATHLEN) {
|
||||
buffer[n] = SEP;
|
||||
buffer[n + 1] = '\0';
|
||||
}
|
||||
if (wcscat_s(buffer, MAXPATHLEN+1, stuff) != 0)
|
||||
Py_FatalError("buffer overflow in getpathp.c's join()");
|
||||
}
|
||||
|
||||
/* gotlandmark only called by search_for_prefix, which ensures
|
||||
|
@ -181,11 +191,10 @@ static int
|
|||
gotlandmark(wchar_t *landmark)
|
||||
{
|
||||
int ok;
|
||||
Py_ssize_t n;
|
||||
Py_ssize_t n = wcsnlen_s(prefix, MAXPATHLEN);
|
||||
|
||||
n = wcslen(prefix);
|
||||
join(prefix, landmark);
|
||||
ok = ismodule(prefix);
|
||||
ok = ismodule(prefix, FALSE);
|
||||
prefix[n] = '\0';
|
||||
return ok;
|
||||
}
|
||||
|
@ -196,7 +205,7 @@ static int
|
|||
search_for_prefix(wchar_t *argv0_path, wchar_t *landmark)
|
||||
{
|
||||
/* Search from argv0_path, until landmark is found */
|
||||
wcscpy(prefix, argv0_path);
|
||||
wcscpy_s(prefix, MAXPATHLEN + 1, argv0_path);
|
||||
do {
|
||||
if (gotlandmark(landmark))
|
||||
return 1;
|
||||
|
@ -236,7 +245,7 @@ getpythonregpath(HKEY keyBase, int skipcore)
|
|||
WCHAR *dataBuf = NULL;
|
||||
static const WCHAR keyPrefix[] = L"Software\\Python\\PythonCore\\";
|
||||
static const WCHAR keySuffix[] = L"\\PythonPath";
|
||||
size_t versionLen;
|
||||
size_t versionLen, keyBufLen;
|
||||
DWORD index;
|
||||
WCHAR *keyBuf = NULL;
|
||||
WCHAR *keyBufPtr;
|
||||
|
@ -245,12 +254,13 @@ getpythonregpath(HKEY keyBase, int skipcore)
|
|||
/* Tried to use sysget("winver") but here is too early :-( */
|
||||
versionLen = strlen(PyWin_DLLVersionString);
|
||||
/* Space for all the chars, plus one \0 */
|
||||
keyBuf = keyBufPtr = PyMem_RawMalloc(sizeof(keyPrefix) +
|
||||
sizeof(WCHAR)*(versionLen-1) +
|
||||
sizeof(keySuffix));
|
||||
keyBufLen = sizeof(keyPrefix) +
|
||||
sizeof(WCHAR)*(versionLen-1) +
|
||||
sizeof(keySuffix);
|
||||
keyBuf = keyBufPtr = PyMem_RawMalloc(keyBufLen);
|
||||
if (keyBuf==NULL) goto done;
|
||||
|
||||
memcpy(keyBufPtr, keyPrefix, sizeof(keyPrefix)-sizeof(WCHAR));
|
||||
memcpy_s(keyBufPtr, keyBufLen, keyPrefix, sizeof(keyPrefix)-sizeof(WCHAR));
|
||||
keyBufPtr += Py_ARRAY_LENGTH(keyPrefix) - 1;
|
||||
mbstowcs(keyBufPtr, PyWin_DLLVersionString, versionLen);
|
||||
keyBufPtr += versionLen;
|
||||
|
@ -484,7 +494,7 @@ calculate_path(void)
|
|||
wchar_t *machinepath = NULL;
|
||||
wchar_t *userpath = NULL;
|
||||
wchar_t zip_path[MAXPATHLEN+1];
|
||||
size_t len;
|
||||
int applocal = 0;
|
||||
|
||||
if (!Py_IgnoreEnvironmentFlag) {
|
||||
envpath = _wgetenv(L"PYTHONPATH");
|
||||
|
@ -502,7 +512,7 @@ calculate_path(void)
|
|||
|
||||
get_progpath();
|
||||
/* progpath guaranteed \0 terminated in MAXPATH+1 bytes. */
|
||||
wcscpy(argv0_path, progpath);
|
||||
wcscpy_s(argv0_path, MAXPATHLEN+1, progpath);
|
||||
reduce(argv0_path);
|
||||
|
||||
/* Search for an environment configuration file, first in the
|
||||
|
@ -511,27 +521,39 @@ calculate_path(void)
|
|||
*/
|
||||
|
||||
{
|
||||
wchar_t envbuffer[MAXPATHLEN+1];
|
||||
wchar_t tmpbuffer[MAXPATHLEN+1];
|
||||
wchar_t *env_cfg = L"pyvenv.cfg";
|
||||
const wchar_t *env_cfg = L"pyvenv.cfg";
|
||||
FILE * env_file = NULL;
|
||||
|
||||
wcscpy(tmpbuffer, argv0_path);
|
||||
join(tmpbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(tmpbuffer, L"r");
|
||||
wcscpy_s(envbuffer, MAXPATHLEN+1, argv0_path);
|
||||
join(envbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(envbuffer, L"r");
|
||||
if (env_file == NULL) {
|
||||
errno = 0;
|
||||
reduce(tmpbuffer);
|
||||
reduce(tmpbuffer);
|
||||
join(tmpbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(tmpbuffer, L"r");
|
||||
reduce(envbuffer);
|
||||
reduce(envbuffer);
|
||||
join(envbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(envbuffer, L"r");
|
||||
if (env_file == NULL) {
|
||||
errno = 0;
|
||||
}
|
||||
}
|
||||
if (env_file != NULL) {
|
||||
/* Look for an 'applocal' variable and, if true, ignore all registry
|
||||
* keys and environment variables, but retain the default paths
|
||||
* (DLLs, Lib) and the zip file. Setting pythonhome here suppresses
|
||||
* the search for LANDMARK below and overrides %PYTHONHOME%.
|
||||
*/
|
||||
if (find_env_config_value(env_file, L"applocal", tmpbuffer) &&
|
||||
(applocal = (wcsicmp(tmpbuffer, L"true") == 0))) {
|
||||
envpath = NULL;
|
||||
pythonhome = argv0_path;
|
||||
}
|
||||
|
||||
/* Look for a 'home' variable and set argv0_path to it, if found */
|
||||
if (find_env_config_value(env_file, L"home", tmpbuffer)) {
|
||||
wcscpy(argv0_path, tmpbuffer);
|
||||
wcscpy_s(argv0_path, MAXPATHLEN+1, tmpbuffer);
|
||||
}
|
||||
fclose(env_file);
|
||||
env_file = NULL;
|
||||
|
@ -545,33 +567,30 @@ calculate_path(void)
|
|||
pythonhome = NULL;
|
||||
}
|
||||
else
|
||||
wcsncpy(prefix, pythonhome, MAXPATHLEN);
|
||||
wcscpy_s(prefix, MAXPATHLEN+1, pythonhome);
|
||||
|
||||
if (envpath && *envpath == '\0')
|
||||
envpath = NULL;
|
||||
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
/* Calculate zip archive path */
|
||||
if (dllpath[0]) /* use name of python DLL */
|
||||
wcsncpy(zip_path, dllpath, MAXPATHLEN);
|
||||
else /* use name of executable program */
|
||||
wcsncpy(zip_path, progpath, MAXPATHLEN);
|
||||
zip_path[MAXPATHLEN] = '\0';
|
||||
len = wcslen(zip_path);
|
||||
if (len > 4) {
|
||||
zip_path[len-3] = 'z'; /* change ending to "zip" */
|
||||
zip_path[len-2] = 'i';
|
||||
zip_path[len-1] = 'p';
|
||||
}
|
||||
/* Calculate zip archive path from DLL or exe path */
|
||||
if (wcscpy_s(zip_path, MAXPATHLEN+1, dllpath[0] ? dllpath : progpath))
|
||||
/* exceeded buffer length - ignore zip_path */
|
||||
zip_path[0] = '\0';
|
||||
else {
|
||||
zip_path[0] = 0;
|
||||
wchar_t *dot = wcsrchr(zip_path, '.');
|
||||
if (!dot || wcscpy_s(dot, MAXPATHLEN+1 - (dot - zip_path), L".zip"))
|
||||
/* exceeded buffer length - ignore zip_path */
|
||||
zip_path[0] = L'\0';
|
||||
}
|
||||
|
||||
skiphome = pythonhome==NULL ? 0 : 1;
|
||||
#ifdef Py_ENABLE_SHARED
|
||||
machinepath = getpythonregpath(HKEY_LOCAL_MACHINE, skiphome);
|
||||
userpath = getpythonregpath(HKEY_CURRENT_USER, skiphome);
|
||||
if (!applocal) {
|
||||
machinepath = getpythonregpath(HKEY_LOCAL_MACHINE, skiphome);
|
||||
userpath = getpythonregpath(HKEY_CURRENT_USER, skiphome);
|
||||
}
|
||||
#endif
|
||||
/* We only use the default relative PYTHONPATH if we havent
|
||||
anything better to use! */
|
||||
|
@ -590,6 +609,7 @@ calculate_path(void)
|
|||
Extra rules:
|
||||
- If PYTHONHOME is set (in any way) item (3) is ignored.
|
||||
- If registry values are used, (4) and (5) are ignored.
|
||||
- If applocal is set, (1), (3), and registry values are ignored
|
||||
*/
|
||||
|
||||
/* Calculate size of return buffer */
|
||||
|
@ -600,21 +620,21 @@ calculate_path(void)
|
|||
if (*p == DELIM)
|
||||
bufsz++; /* number of DELIM plus one */
|
||||
}
|
||||
bufsz *= wcslen(pythonhome);
|
||||
bufsz *= wcsnlen_s(pythonhome, MAXPATHLEN+1);
|
||||
}
|
||||
else
|
||||
bufsz = 0;
|
||||
bufsz += wcslen(PYTHONPATH) + 1;
|
||||
bufsz += wcslen(argv0_path) + 1;
|
||||
bufsz += wcsnlen_s(PYTHONPATH, MAXPATHLEN+1) + 1;
|
||||
bufsz += wcsnlen_s(argv0_path, MAXPATHLEN+1) + 1;
|
||||
#ifdef MS_WINDOWS
|
||||
if (userpath)
|
||||
bufsz += wcslen(userpath) + 1;
|
||||
if (machinepath)
|
||||
bufsz += wcslen(machinepath) + 1;
|
||||
bufsz += wcslen(zip_path) + 1;
|
||||
if (!applocal && userpath)
|
||||
bufsz += wcsnlen_s(userpath, MAXPATHLEN+1) + 1;
|
||||
if (!applocal && machinepath)
|
||||
bufsz += wcsnlen_s(machinepath, MAXPATHLEN+1) + 1;
|
||||
bufsz += wcsnlen_s(zip_path, MAXPATHLEN+1) + 1;
|
||||
#endif
|
||||
if (envpath != NULL)
|
||||
bufsz += wcslen(envpath) + 1;
|
||||
bufsz += wcsnlen_s(envpath, MAXPATHLEN+1) + 1;
|
||||
|
||||
module_search_path = buf = PyMem_RawMalloc(bufsz*sizeof(wchar_t));
|
||||
if (buf == NULL) {
|
||||
|
@ -636,38 +656,45 @@ calculate_path(void)
|
|||
}
|
||||
|
||||
if (envpath) {
|
||||
wcscpy(buf, envpath);
|
||||
if (wcscpy_s(buf, bufsz - (buf - module_search_path), envpath))
|
||||
Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
|
||||
buf = wcschr(buf, L'\0');
|
||||
*buf++ = DELIM;
|
||||
}
|
||||
#ifdef MS_WINDOWS
|
||||
if (zip_path[0]) {
|
||||
wcscpy(buf, zip_path);
|
||||
if (wcscpy_s(buf, bufsz - (buf - module_search_path), zip_path))
|
||||
Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
|
||||
buf = wcschr(buf, L'\0');
|
||||
*buf++ = DELIM;
|
||||
}
|
||||
if (userpath) {
|
||||
wcscpy(buf, userpath);
|
||||
if (wcscpy_s(buf, bufsz - (buf - module_search_path), userpath))
|
||||
Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
|
||||
buf = wcschr(buf, L'\0');
|
||||
*buf++ = DELIM;
|
||||
PyMem_RawFree(userpath);
|
||||
}
|
||||
if (machinepath) {
|
||||
wcscpy(buf, machinepath);
|
||||
if (wcscpy_s(buf, bufsz - (buf - module_search_path), machinepath))
|
||||
Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
|
||||
buf = wcschr(buf, L'\0');
|
||||
*buf++ = DELIM;
|
||||
PyMem_RawFree(machinepath);
|
||||
}
|
||||
if (pythonhome == NULL) {
|
||||
if (!skipdefault) {
|
||||
wcscpy(buf, PYTHONPATH);
|
||||
if (wcscpy_s(buf, bufsz - (buf - module_search_path), PYTHONPATH))
|
||||
Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
|
||||
buf = wcschr(buf, L'\0');
|
||||
*buf++ = DELIM;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (pythonhome == NULL) {
|
||||
wcscpy(buf, PYTHONPATH);
|
||||
buf = wcschr(buf, L'\0');
|
||||
*buf++ = DELIM;
|
||||
}
|
||||
#endif /* MS_WINDOWS */
|
||||
else {
|
||||
|
@ -681,25 +708,26 @@ calculate_path(void)
|
|||
else
|
||||
n = q-p;
|
||||
if (p[0] == '.' && is_sep(p[1])) {
|
||||
wcscpy(buf, pythonhome);
|
||||
if (wcscpy_s(buf, bufsz - (buf - module_search_path), pythonhome))
|
||||
Py_FatalError("buffer overflow in getpathp.c's calculate_path()");
|
||||
buf = wcschr(buf, L'\0');
|
||||
p++;
|
||||
n--;
|
||||
}
|
||||
wcsncpy(buf, p, n);
|
||||
buf += n;
|
||||
*buf++ = DELIM;
|
||||
if (q == NULL)
|
||||
break;
|
||||
*buf++ = DELIM;
|
||||
p = q+1;
|
||||
}
|
||||
}
|
||||
if (argv0_path) {
|
||||
*buf++ = DELIM;
|
||||
wcscpy(buf, argv0_path);
|
||||
buf = wcschr(buf, L'\0');
|
||||
*buf++ = DELIM;
|
||||
}
|
||||
*buf = L'\0';
|
||||
*(buf - 1) = L'\0';
|
||||
/* Now to pull one last hack/trick. If sys.prefix is
|
||||
empty, then try and find it somewhere on the paths
|
||||
we calculated. We scan backwards, as our general policy
|
||||
|
|
|
@ -149,6 +149,9 @@ def main():
|
|||
copied = copy_to_layout(temp / t.rstrip('/'), rglob(s, p, c))
|
||||
print('Copied {} files'.format(copied))
|
||||
|
||||
with open(str(temp / 'pyvenv.cfg'), 'w') as f:
|
||||
print('applocal = true', file=f)
|
||||
|
||||
total = copy_to_layout(out, rglob(temp, '*', None))
|
||||
print('Wrote {} files to {}'.format(total, out))
|
||||
finally:
|
||||
|
|
Loading…
Reference in New Issue