# 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; }