cpython: b79d276041a8 (original) (raw)
Mercurial > cpython
changeset 78160:b79d276041a8
Closes #15307: symlinks now work on OS X with framework Python builds. Patch by Ronald Oussoren. [#15307]
Vinay Sajip <vinay_sajip@yahoo.co.uk> | |
---|---|
date | Tue, 17 Jul 2012 17:33:46 +0100 |
parents | fbda36216f24 |
children | af7961e1c362 |
files | Doc/library/venv.rst Lib/test/test_venv.py Lib/venv/__init__.py Mac/Tools/pythonw.c Modules/getpath.c Modules/main.c |
diffstat | 6 files changed, 115 insertions(+), 27 deletions(-)[+] [-] Doc/library/venv.rst 2 Lib/test/test_venv.py 52 Lib/venv/__init__.py 10 Mac/Tools/pythonw.c 40 Modules/getpath.c 16 Modules/main.c 22 |
line wrap: on
line diff
--- a/Doc/library/venv.rst
+++ b/Doc/library/venv.rst
@@ -81,7 +81,7 @@ creation according to their needs, the :
* symlinks
-- a Boolean value indicating whether to attempt to symlink the
Python binary (and any necessary DLLs or other binaries,
e.g. pythonw.exe
), rather than copying. Defaults to True
on Linux and
Unix systems, but ``False`` on Windows and Mac OS X.[](#l1.7)
Unix systems, but ``False`` on Windows.[](#l1.8)
* upgrade
-- a Boolean value which, if True, will upgrade an existing
environment with the running Python - for use when that Python has been
--- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -154,17 +154,47 @@ class BasicTest(BaseTest): """ for usl in (False, True): builder = venv.EnvBuilder(clear=True, symlinks=usl)
if (usl and sys.platform == 'darwin' and[](#l2.7)
'__PYVENV_LAUNCHER__' in os.environ):[](#l2.8)
self.assertRaises(ValueError, builder.create, self.env_dir)[](#l2.9)
else:[](#l2.10)
builder.create(self.env_dir)[](#l2.11)
fn = self.get_env_file(self.bindir, self.exe)[](#l2.12)
# Don't test when False, because e.g. 'python' is always[](#l2.13)
# symlinked to 'python3.3' in the env, even when symlinking in[](#l2.14)
# general isn't wanted.[](#l2.15)
if usl:[](#l2.16)
self.assertTrue(os.path.islink(fn))[](#l2.17)
builder.create(self.env_dir)[](#l2.18)
fn = self.get_env_file(self.bindir, self.exe)[](#l2.19)
# Don't test when False, because e.g. 'python' is always[](#l2.20)
# symlinked to 'python3.3' in the env, even when symlinking in[](#l2.21)
# general isn't wanted.[](#l2.22)
if usl:[](#l2.23)
self.assertTrue(os.path.islink(fn))[](#l2.24)
If a venv is created from a source build and that venv is used to
run the test, the pyvenv.cfg in the venv created in the test will
point to the venv being used to run the test, and we lose the link
to the source build - so Python can't initialise properly.
- @unittest.skipIf(sys.prefix != sys.base_prefix, 'Test not appropriate '
'in a venv')[](#l2.31)
- def test_executable(self):
"""[](#l2.33)
Test that the sys.executable value is as expected.[](#l2.34)
"""[](#l2.35)
shutil.rmtree(self.env_dir)[](#l2.36)
self.run_with_capture(venv.create, self.env_dir)[](#l2.37)
envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)[](#l2.38)
cmd = [envpy, '-c', 'import sys; print(sys.executable)'][](#l2.39)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,[](#l2.40)
stderr=subprocess.PIPE)[](#l2.41)
out, err = p.communicate()[](#l2.42)
self.assertEqual(out[:-1], envpy.encode())[](#l2.43)
- @unittest.skipUnless(can_symlink(), 'Needs symlinks')
- def test_executable_symlinks(self):
"""[](#l2.47)
Test that the sys.executable value is as expected.[](#l2.48)
"""[](#l2.49)
shutil.rmtree(self.env_dir)[](#l2.50)
builder = venv.EnvBuilder(clear=True, symlinks=True)[](#l2.51)
builder.create(self.env_dir)[](#l2.52)
envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)[](#l2.53)
cmd = [envpy, '-c', 'import sys; print(sys.executable)'][](#l2.54)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,[](#l2.55)
stderr=subprocess.PIPE)[](#l2.56)
out, err = p.communicate()[](#l2.57)
self.assertEqual(out[:-1], envpy.encode())[](#l2.58)
def test_main(): run_unittest(BasicTest)
--- a/Lib/venv/init.py +++ b/Lib/venv/init.py @@ -82,13 +82,6 @@ class EnvBuilder: :param env_dir: The target directory to create an environment in. """
if (self.symlinks and[](#l3.7)
sys.platform == 'darwin' and[](#l3.8)
sysconfig.get_config_var('PYTHONFRAMEWORK')):[](#l3.9)
# Symlinking the stub executable in an OSX framework build will[](#l3.10)
# result in a broken virtual environment.[](#l3.11)
raise ValueError([](#l3.12)
'Symlinking is not supported on OSX framework Python.')[](#l3.13) env_dir = os.path.abspath(env_dir)[](#l3.14) context = self.ensure_directories(env_dir)[](#l3.15) self.create_configuration(context)[](#l3.16)
@@ -366,8 +359,7 @@ def main(args=None): action='store_true', dest='system_site', help='Give the virtual environment access to the ' 'system site-packages dir.')
if os.name == 'nt' or (sys.platform == 'darwin' and[](#l3.21)
sysconfig.get_config_var('PYTHONFRAMEWORK')):[](#l3.22)
if os.name == 'nt':[](#l3.23) use_symlinks = False[](#l3.24) else:[](#l3.25) use_symlinks = True[](#l3.26)
--- a/Mac/Tools/pythonw.c +++ b/Mac/Tools/pythonw.c @@ -28,6 +28,7 @@ #include <dlfcn.h> #include <stdlib.h> #include <Python.h> +#include <mach-o/dyld.h> extern char** environ; @@ -158,9 +159,44 @@ main(int argc, char *argv) { / Set the original executable path in the environment. */ status = _NSGetExecutablePath(path, &size); if (status == 0) {
if (realpath(path, real_path) != NULL) {[](#l4.15)
setenv("__PYVENV_LAUNCHER__", real_path, 1);[](#l4.16)
/*[](#l4.17)
* Note: don't call 'realpath', that will[](#l4.18)
* erase symlink information, and that[](#l4.19)
* breaks "pyvenv --symlink"[](#l4.20)
*[](#l4.21)
* It is nice to have the directory name[](#l4.22)
* as a cleaned up absolute path though,[](#l4.23)
* therefore call realpath on dirname(path)[](#l4.24)
*/[](#l4.25)
char* slash = strrchr(path, '/');[](#l4.26)
if (slash) {[](#l4.27)
char replaced;[](#l4.28)
replaced = slash[1];[](#l4.29)
slash[1] = 0;[](#l4.30)
if (realpath(path, real_path) == NULL) {[](#l4.31)
err(1, "realpath: %s", path);[](#l4.32)
}[](#l4.33)
slash[1] = replaced;[](#l4.34)
if (strlcat(real_path, slash, sizeof(real_path)) > sizeof(real_path)) {[](#l4.35)
errno = EINVAL;[](#l4.36)
err(1, "realpath: %s", path);[](#l4.37)
}[](#l4.38)
} else {[](#l4.40)
if (realpath(".", real_path) == NULL) {[](#l4.41)
err(1, "realpath: %s", path);[](#l4.42)
}[](#l4.43)
if (strlcat(real_path, "/", sizeof(real_path)) > sizeof(real_path)) {[](#l4.44)
errno = EINVAL;[](#l4.45)
err(1, "realpath: %s", path);[](#l4.46)
}[](#l4.47)
if (strlcat(real_path, path, sizeof(real_path)) > sizeof(real_path)) {[](#l4.48)
errno = EINVAL;[](#l4.49)
err(1, "realpath: %s", path);[](#l4.50)
}[](#l4.51) }[](#l4.52)
--- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -474,6 +474,7 @@ calculate_path(void) wchar_t *defpath; #ifdef WITH_NEXT_FRAMEWORK NSModule pythonModule;
#endif #ifdef APPLE #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 @@ -568,8 +569,8 @@ calculate_path(void) / pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize")); / Use dylib functions to find out where the framework was loaded from */
- modPath = NSLibraryNameForModule(pythonModule);
- if (modPath != NULL) { /* We're in a framework. / / See if we might be in the build directory. The framework in the ** build directory is incomplete, it only has the .dylib and a few
@@ -578,7 +579,12 @@ calculate_path(void) ** be running the interpreter in the build directory, so we use the ** build-directory-specific logic to find Lib and such. */
wcsncpy(argv0_path, buf, MAXPATHLEN);[](#l5.26)
wchar_t* wbuf = _Py_char2wchar(modPath, NULL);[](#l5.27)
if (wbuf == NULL) {[](#l5.28)
Py_FatalError("Cannot decode framework location");[](#l5.29)
}[](#l5.30)
wcsncpy(argv0_path, wbuf, MAXPATHLEN);[](#l5.32) reduce(argv0_path);[](#l5.33) joinpath(argv0_path, lib_python);[](#l5.34) joinpath(argv0_path, LANDMARK);[](#l5.35)
@@ -589,8 +595,9 @@ calculate_path(void) } else { /* Use the location of the library as the progpath */
wcsncpy(argv0_path, buf, MAXPATHLEN);[](#l5.40)
#endif @@ -629,6 +636,7 @@ calculate_path(void) FILE * env_file = NULL; wcscpy(tmpbuffer, argv0_path); + joinpath(tmpbuffer, env_cfg); env_file = _Py_wfopen(tmpbuffer, L"r"); if (env_file == NULL) {
--- a/Modules/main.c +++ b/Modules/main.c @@ -616,7 +616,29 @@ Py_Main(int argc, wchar_t *argv) Py_SetProgramName(buffer); / buffer is now handed off - do not free */ } else { +#ifdef WITH_NEXT_FRAMEWORK
char* pyvenv_launcher = getenv("__PYVENV_LAUNCHER__");[](#l6.8)
if (pyvenv_launcher && *pyvenv_launcher) {[](#l6.10)
/* Used by Mac/Tools/pythonw.c to forward[](#l6.11)
* the argv0 of the stub executable[](#l6.12)
*/[](#l6.13)
wchar_t* wbuf = _Py_char2wchar(pyvenv_launcher, NULL);[](#l6.14)
if (wbuf == NULL) {[](#l6.16)
Py_FatalError("Cannot decode __PYVENV_LAUNCHER__");[](#l6.17)
}[](#l6.18)
Py_SetProgramName(wbuf);[](#l6.19)
/* Don't free wbuf, the argument to Py_SetProgramName[](#l6.21)
* must remain valid until the Py_Finalize is called.[](#l6.22)
*/[](#l6.23)
} else {[](#l6.24)
Py_SetProgramName(argv[0]);[](#l6.25)
}[](#l6.26)
+#else Py_SetProgramName(argv[0]); +#endif } #else Py_SetProgramName(argv[0]);