summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Doc/library/venv.rst2
-rw-r--r--Lib/test/test_venv.py52
-rw-r--r--Lib/venv/__init__.py10
-rw-r--r--Mac/Tools/pythonw.c40
-rw-r--r--Modules/getpath.c16
-rw-r--r--Modules/main.c22
6 files changed, 115 insertions, 27 deletions
diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst
index 592a0d9cc6..24999621a1 100644
--- a/Doc/library/venv.rst
+++ b/Doc/library/venv.rst
@@ -81,7 +81,7 @@ creation according to their needs, the :class:`EnvBuilder` class.
* ``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.
+ Unix systems, but ``False`` on Windows.
* ``upgrade`` -- a Boolean value which, if True, will upgrade an existing
environment with the running Python - for use when that Python has been
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 6f73fcf3d0..b1b831e3a3 100644
--- 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
- '__PYVENV_LAUNCHER__' in os.environ):
- self.assertRaises(ValueError, builder.create, self.env_dir)
- else:
- builder.create(self.env_dir)
- fn = self.get_env_file(self.bindir, self.exe)
- # Don't test when False, because e.g. 'python' is always
- # symlinked to 'python3.3' in the env, even when symlinking in
- # general isn't wanted.
- if usl:
- self.assertTrue(os.path.islink(fn))
+ builder.create(self.env_dir)
+ fn = self.get_env_file(self.bindir, self.exe)
+ # Don't test when False, because e.g. 'python' is always
+ # symlinked to 'python3.3' in the env, even when symlinking in
+ # general isn't wanted.
+ if usl:
+ self.assertTrue(os.path.islink(fn))
+
+ # 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')
+ def test_executable(self):
+ """
+ Test that the sys.executable value is as expected.
+ """
+ shutil.rmtree(self.env_dir)
+ self.run_with_capture(venv.create, self.env_dir)
+ envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
+ cmd = [envpy, '-c', 'import sys; print(sys.executable)']
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = p.communicate()
+ self.assertEqual(out[:-1], envpy.encode())
+
+ @unittest.skipUnless(can_symlink(), 'Needs symlinks')
+ def test_executable_symlinks(self):
+ """
+ Test that the sys.executable value is as expected.
+ """
+ shutil.rmtree(self.env_dir)
+ builder = venv.EnvBuilder(clear=True, symlinks=True)
+ builder.create(self.env_dir)
+ envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
+ cmd = [envpy, '-c', 'import sys; print(sys.executable)']
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = p.communicate()
+ self.assertEqual(out[:-1], envpy.encode())
def test_main():
run_unittest(BasicTest)
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
index 38ed54c88a..8d2deb7385 100644
--- 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
- sys.platform == 'darwin' and
- sysconfig.get_config_var('PYTHONFRAMEWORK')):
- # Symlinking the stub executable in an OSX framework build will
- # result in a broken virtual environment.
- raise ValueError(
- 'Symlinking is not supported on OSX framework Python.')
env_dir = os.path.abspath(env_dir)
context = self.ensure_directories(env_dir)
self.create_configuration(context)
@@ -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
- sysconfig.get_config_var('PYTHONFRAMEWORK')):
+ if os.name == 'nt':
use_symlinks = False
else:
use_symlinks = True
diff --git a/Mac/Tools/pythonw.c b/Mac/Tools/pythonw.c
index f5cdff52d0..1d2db383f9 100644
--- 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) {
- setenv("__PYVENV_LAUNCHER__", real_path, 1);
+ /*
+ * Note: don't call 'realpath', that will
+ * erase symlink information, and that
+ * breaks "pyvenv --symlink"
+ *
+ * It is nice to have the directory name
+ * as a cleaned up absolute path though,
+ * therefore call realpath on dirname(path)
+ */
+ char* slash = strrchr(path, '/');
+ if (slash) {
+ char replaced;
+ replaced = slash[1];
+ slash[1] = 0;
+ if (realpath(path, real_path) == NULL) {
+ err(1, "realpath: %s", path);
+ }
+ slash[1] = replaced;
+ if (strlcat(real_path, slash, sizeof(real_path)) > sizeof(real_path)) {
+ errno = EINVAL;
+ err(1, "realpath: %s", path);
+ }
+
+ } else {
+ if (realpath(".", real_path) == NULL) {
+ err(1, "realpath: %s", path);
+ }
+ if (strlcat(real_path, "/", sizeof(real_path)) > sizeof(real_path)) {
+ errno = EINVAL;
+ err(1, "realpath: %s", path);
+ }
+ if (strlcat(real_path, path, sizeof(real_path)) > sizeof(real_path)) {
+ errno = EINVAL;
+ err(1, "realpath: %s", path);
+ }
}
+
+ setenv("__PYVENV_LAUNCHER__", real_path, 1);
}
/*
diff --git a/Modules/getpath.c b/Modules/getpath.c
index b15319723e..b98c52012d 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -474,6 +474,7 @@ calculate_path(void)
wchar_t *defpath;
#ifdef WITH_NEXT_FRAMEWORK
NSModule pythonModule;
+ const char* modPath;
#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 */
- buf = (wchar_t *)NSLibraryNameForModule(pythonModule);
- if (buf != NULL) {
+ 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);
+ wchar_t* wbuf = _Py_char2wchar(modPath, NULL);
+ if (wbuf == NULL) {
+ Py_FatalError("Cannot decode framework location");
+ }
+
+ wcsncpy(argv0_path, wbuf, MAXPATHLEN);
reduce(argv0_path);
joinpath(argv0_path, lib_python);
joinpath(argv0_path, LANDMARK);
@@ -589,8 +595,9 @@ calculate_path(void)
}
else {
/* Use the location of the library as the progpath */
- wcsncpy(argv0_path, buf, MAXPATHLEN);
+ wcsncpy(argv0_path, wbuf, MAXPATHLEN);
}
+ PyMem_Free(wbuf);
}
#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) {
diff --git a/Modules/main.c b/Modules/main.c
index 64bac6e9aa..e86aa77b65 100644
--- 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__");
+
+ if (pyvenv_launcher && *pyvenv_launcher) {
+ /* Used by Mac/Tools/pythonw.c to forward
+ * the argv0 of the stub executable
+ */
+ wchar_t* wbuf = _Py_char2wchar(pyvenv_launcher, NULL);
+
+ if (wbuf == NULL) {
+ Py_FatalError("Cannot decode __PYVENV_LAUNCHER__");
+ }
+ Py_SetProgramName(wbuf);
+
+ /* Don't free wbuf, the argument to Py_SetProgramName
+ * must remain valid until the Py_Finalize is called.
+ */
+ } else {
+ Py_SetProgramName(argv[0]);
+ }
+#else
Py_SetProgramName(argv[0]);
+#endif
}
#else
Py_SetProgramName(argv[0]);