# HG changeset patch
# User Paul Moore
# Date 1423925718 0
# Sat Feb 14 14:55:18 2015 +0000
# Node ID 118316d07172c036fec2963fede44df08057517e
# Parent ed9e9e6b3c1e9f7b6b1a9843d3d0ac088998b390
Implement PEP 486 - Make the Python Launcher aware of virtual environments
diff -r ed9e9e6b3c1e -r 118316d07172 Doc/using/windows.rst
--- a/Doc/using/windows.rst Fri Feb 13 21:02:12 2015 +0200
+++ b/Doc/using/windows.rst Sat Feb 14 14:55:18 2015 +0000
@@ -404,6 +404,16 @@
Per-user installations of Python do not add the launcher to :envvar:`PATH`
unless the option was selected on installation.
+Virtual environments
+^^^^^^^^^^^^^^^^^^^^
+
+If the launcher is run with no explicit Python version specification, and a
+virtual environment (created with the standard library :mod:`venv` module or
+the external ``virtualenv`` tool) active, the launcher will run the virtual
+environment's interpreter rather than the global one. To run the global
+interpreter, either deactivate the virtual environment, or explicitly specify
+the global Python version.
+
From a script
^^^^^^^^^^^^^
@@ -478,6 +488,16 @@
on Windows which you hope will be useful on Unix, you should use one of the
shebang lines starting with ``/usr``.
+Any of the above virtual commands can be suffixed with an explicit version
+(either just the major version, or the major and minor version) - for example
+``/usr/bin/python2.7`` - which will cause that specific version to be located
+and used.
+
+The ``/usr/bin/env`` form of shebang line has one further special property.
+Before looking for installed Python interpreters, this form will search the
+executable :envvar:`PATH` for a Python executable. This corresponds to the
+behaviour of the Unix ``env`` program, which performs a :envvar:`PATH` search.
+
Arguments in shebang lines
--------------------------
diff -r ed9e9e6b3c1e -r 118316d07172 PC/launcher.c
--- a/PC/launcher.c Fri Feb 13 21:02:12 2015 +0200
+++ b/PC/launcher.c Sat Feb 14 14:55:18 2015 +0000
@@ -384,6 +384,31 @@
}
+static wchar_t *
+find_python_by_venv()
+{
+ static wchar_t venv_python[MAX_PATH];
+ wchar_t *virtual_env = get_env(L"VIRTUAL_ENV");
+ DWORD attrs;
+
+ /* Check for VIRTUAL_ENV environment variable */
+ if (virtual_env == NULL || virtual_env[0] == L'\0') {
+ return NULL;
+ }
+
+ /* Check for a python executable in the venv */
+ debug(L"Checking for Python executable in virtual env '%ls'\n", virtual_env);
+ _snwprintf_s(venv_python, MAX_PATH, _TRUNCATE,
+ L"%ls\\Scripts\\%ls", virtual_env, PYTHON_EXECUTABLE);
+ attrs = GetFileAttributesW(venv_python);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ debug(L"Python executable %ls missing from virtual env\n", venv_python);
+ return NULL;
+ }
+
+ return venv_python;
+}
+
static wchar_t appdata_ini_path[MAX_PATH];
static wchar_t launcher_ini_path[MAX_PATH];
@@ -1309,6 +1334,7 @@
{
wchar_t * wp;
wchar_t * command;
+ wchar_t * executable;
wchar_t * p;
int rc = 0;
size_t plen;
@@ -1453,6 +1479,7 @@
if (ip == NULL)
error(RC_NO_PYTHON, L"Requested Python version (%ls) not \
installed", &p[1]);
+ executable = ip->executable;
command += wcslen(p);
command = skip_whitespace(command);
}
@@ -1470,9 +1497,16 @@
#endif
if (!valid) {
- ip = locate_python(L"");
- if (ip == NULL)
- error(RC_NO_PYTHON, L"Can't find a default Python.");
+ /* Look for an active virtualenv */
+ executable = find_python_by_venv();
+
+ /* If we didn't find one, look for the default Python */
+ if (executable == NULL) {
+ ip = locate_python(L"");
+ if (ip == NULL)
+ error(RC_NO_PYTHON, L"Can't find a default Python.");
+ executable = ip->executable;
+ }
if ((argc == 2) && (!_wcsicmp(p, L"-h") || !_wcsicmp(p, L"--help"))) {
#if defined(_M_X64)
BOOL canDo64bit = TRUE;
@@ -1500,7 +1534,7 @@
fflush(stdout);
}
}
- invoke_child(ip->executable, NULL, command);
+ invoke_child(executable, NULL, command);
return rc;
}