Issue #23465: Implement PEP 486 - Make the Python Launcher aware of virtual environments (patch by Paul Moore)
diff --git a/PC/launcher.c b/PC/launcher.c
index d450d9f..33dd5da 100644
--- a/PC/launcher.c
+++ b/PC/launcher.c
@@ -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;
 }