Repository: AgnostiqHQ/covalent
Pull request #1652 (2023-05-17 10:03:34)

use correct python executable
Fixes https://github.com/AgnostiqHQ/covalent/issues/1649

- [x] I have added the tests to cover my changes.
- [x] I have updated the documentation and CHANGELOG accordingly.
- [x] I have read the CONTRIBUTING document.

I guess we can install `conda` on CI so that the test runs...

CC: @AlejandroEsquivel / @santoshkumarradha / @kessler-frost 

madhur-tandon: @kessler-frost you will have to approve again since I resolved conflict with Changelog...

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index db48bdf9..0f817489 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [UNRELEASED]
 
+### Fixed
+
+- Resolving correct python executable
+
 ### Added
 
 - `executor` property to `Electron` class, allowing updation of executor after electron function definition
diff --git a/covalent_dispatcher/_cli/service.py b/covalent_dispatcher/_cli/service.py
index 4be089b5..a19cec66 100644
--- a/covalent_dispatcher/_cli/service.py
+++ b/covalent_dispatcher/_cli/service.py
@@ -27,6 +27,7 @@ import json
 import os
 import shutil
 import socket
+import sys
 import time
 from pathlib import Path
 from subprocess import DEVNULL, Popen
@@ -198,7 +199,7 @@ def _graceful_start(
     no_cluster_flag = "--no-cluster" if no_cluster else ""
 
     port = _next_available_port(port)
-    launch_str = f"{pypath} python app.py {dev_mode_flag} --port {port} {no_cluster_flag} {no_triggers_flag} {triggers_only_flag}>> {logfile} 2>&1"
+    launch_str = f"{pypath} {sys.executable} app.py {dev_mode_flag} --port {port} {no_cluster_flag} {no_triggers_flag} {triggers_only_flag}>> {logfile} 2>&1"
 
     proc = Popen(launch_str, shell=True, stdout=DEVNULL, stderr=DEVNULL, cwd=server_root)
     pid = proc.pid
diff --git a/pyproject.toml b/pyproject.toml
index 400fcbd8..9c5e1b96 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -40,3 +40,6 @@ skip_gitignore = true
 
 [tool.pycln]
 all = true
+
+[tool.pytest.ini_options]
+markers = ["conda: Marks tests that need conda"]


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index c629d69d..21ef8281 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -146,6 +146,16 @@ jobs:
         with:
           python-version: ${{ matrix.python-version }}
 
+      - name: Set up Conda
+        id: install-conda
+        if: env.NEED_PYTHON || env.BUILD_AND_RUN_ALL
+        uses: conda-incubator/setup-miniconda@v2
+        with:
+          miniconda-version: "latest"
+          python-version: ${{ matrix.python-version }}
+          auto-activate-base: true
+          activate-environment: ""
+
       - name: Install Python dependencies
         id: install-python-deps
         if: env.NEED_PYTHON || env.BUILD_AND_RUN_ALL
diff --git a/tests/covalent_dispatcher_tests/_cli/service_test.py b/tests/covalent_dispatcher_tests/_cli/service_test.py
index 29b0cf0c..fb06cb3f 100644
--- a/tests/covalent_dispatcher_tests/_cli/service_test.py
+++ b/tests/covalent_dispatcher_tests/_cli/service_test.py
@@ -20,7 +20,11 @@
 
 """Tests for Covalent command line interface (CLI) Tool."""
 
+import os
+import subprocess
+import sys
 import tempfile
+import venv
 from unittest import mock
 from unittest.mock import MagicMock, Mock
 
@@ -31,7 +35,6 @@ from click.testing import CliRunner
 from covalent_dispatcher._cli.service import (
     MIGRATION_COMMAND_MSG,
     MIGRATION_WARNING_MSG,
-    UI_LOGFILE,
     _graceful_shutdown,
     _graceful_start,
     _is_server_running,
@@ -54,6 +57,53 @@ STOPPED_SERVER_STATUS_ECHO = "Covalent server is stopped.\n"
 RUNNING_SERVER_STATUS_ECHO = "Covalent server is running at http://localhost:42.\n"
 
 
+def has_conda():
+    try:
+        ret = subprocess.run(["conda"], check=True)
+        return ret.returncode == 0
+    except FileNotFoundError:
+        return False
+
+
+def conda_test(f):
+    mark = pytest.mark.conda
+    skip = pytest.mark.skipif(not has_conda(), reason="conda is unavailable")
+    return mark(skip(f))
+
+
+@conda_test
+def test_python_path_in_conda_env():
+    with tempfile.TemporaryDirectory() as tmp_dir:
+        create_cmd = ["conda", "create", "-y", "--prefix", tmp_dir, "python=3.9.12"]
+        subprocess.run(create_cmd, check=True)
+        check_path_cmd = [
+            "conda",
+            "run",
+            "--prefix",
+            tmp_dir,
+            "python",
+            "-c",
+            "import sys; print(sys.executable)",
+        ]
+        res = subprocess.run(check_path_cmd, check=True, capture_output=True)
+        assert res.stdout.decode().startswith(tmp_dir)
+        check_version_cmd = ["conda", "run", "--prefix", tmp_dir, "python", "--version"]
+        res = subprocess.run(check_version_cmd, check=True, capture_output=True)
+        assert res.stdout.decode().strip() == "Python 3.9.12"
+
+
+def test_python_path_in_venv():
+    with tempfile.TemporaryDirectory() as tmp_dir:
+        venv.create(tmp_dir, with_pip=False)
+        custom_env = os.environ.copy()
+        # equivalent of source venv/bin/activate
+        custom_env["VIRTUAL_ENV"] = tmp_dir
+        custom_env["PATH"] = f"{tmp_dir}/bin:$PATH"
+        check_path_cmd = ["python", "-c", "import sys; print(sys.executable)"]
+        res = subprocess.run(check_path_cmd, check=True, capture_output=True, env=custom_env)
+        assert res.stdout.decode().startswith(tmp_dir)
+
+
 def test_read_pid_nonexistent_file():
     """Test the process id read function when the pid file does not exist."""
 
@@ -169,9 +219,7 @@ def test_graceful_start_when_pid_absent(mocker, no_triggers_flag, triggers_only_
     no_triggers_str = "--no-triggers" if no_triggers_flag else ""
     triggers_only_str = "--triggers-only" if triggers_only_flag else ""
 
-    launch_str = (
-        f" python app.py  --port 1984  {no_triggers_str} {triggers_only_str}>> output.log 2>&1"
-    )
+    launch_str = f" {sys.executable} app.py  --port 1984  {no_triggers_str} {triggers_only_str}>> output.log 2>&1"
 
     with mock.patch("covalent_dispatcher._cli.service.open", mock.mock_open()):
         if no_triggers_flag and triggers_only_flag: