# How Modly Resolves Python Executable Paths from Virtual Environments and System Python

> Learn how Modly finds Python executables from venv or system Python. It prioritizes user args, then virtual environments, and finally system Python for seamless integration.

- Repository: [lightningpixel/modly](https://github.com/lightningpixel/modly)
- Tags: how-to-guide
- Published: 2026-08-21

---

**Modly resolves Python executable paths by prioritizing user-specified arguments, then virtual environments in the API directory or extension folders, and finally falling back to the system Python interpreter that launched the CLI.**

The `lightningpixel/modly` repository implements a robust, platform-aware mechanism to **resolve Python executable paths** across different operating systems and deployment contexts. When starting the API server or spawning isolated extension processes, Modly must locate the correct interpreter—whether it resides in a dedicated **virtual environment** or the system Python installation.

## Resolving Python Paths for the API Server

When executing CLI commands such as `modly-cli serve`, Modly determines the appropriate Python interpreter through a cascading priority system defined in [`tools/modly-cli/agent.py`](https://github.com/lightningpixel/modly/blob/main/tools/modly-cli/agent.py).

### Priority Order for CLI Commands

The `_default_python` function (lines 86-96) implements a four-tier fallback strategy:

1. **User-specified path**: If the `--python` argument is provided, Modly expands and resolves that path immediately using `Path(args.python).expanduser().resolve()`.

2. **API directory virtual environment**: Modly searches for a venv inside the API directory at `api/.venv`, checking `Scripts/python.exe` on Windows or `bin/python` on POSIX systems.

3. **User-wide fallback (Windows only)**: On Windows systems, Modly checks `%APPDATA%/Modly/dependencies/venv/Scripts/python.exe` for a global fallback environment.

4. **System Python**: As a final resort, Modly uses `sys.executable`—the interpreter that launched the CLI process.

The first existing file from this candidate list is returned. If none exist, the CLI raises a `ModlyCliError` to prevent execution with an invalid interpreter.

### The _default_python Implementation

The resolution logic is implemented in [`tools/modly-cli/agent.py`](https://github.com/lightningpixel/modly/blob/main/tools/modly-cli/agent.py):

```python

# From tools/modly-cli/agent.py

candidates = [
    api_dir / ".venv" / "Scripts" / "python.exe",
    api_dir / ".venv" / "bin" / "python",
    # Windows AppData fallback only

    Path(os.environ.get("APPDATA", "")) / "Modly" / "dependencies" / "venv" / "Scripts" / "python.exe",
    Path(sys.executable),
]

```

Modly iterates through these candidates and returns the first path where `path.exists()` returns `True`.

## Isolated Extension Virtual Environments

For **extension subprocesses**, Modly uses a separate resolution strategy that enforces strict isolation. Each extension runs in its own dedicated virtual environment, and the system verifies this environment exists before spawning the subprocess.

### The _venv_python Helper Function

Located in [`api/services/extension_process.py`](https://github.com/lightningpixel/modly/blob/main/api/services/extension_process.py) (lines 30-35), the `_venv_python` function constructs platform-specific paths to extension interpreters:

```python

# From api/services/extension_process.py

def _venv_python(ext_dir: Path) -> Path:
    if sys.platform == "win32":
        return ext_dir / "venv" / "Scripts" / "python.exe"
    return ext_dir / "venv" / "bin" / "python"

```

This helper returns the appropriate path based on the host operating system, ensuring compatibility across Windows and POSIX environments.

### Validation and Error Handling

When `ExtensionProcess._start` initializes, it validates the interpreter path before creating the **subprocess**:

```python
python = _venv_python(self.ext_dir)
if not python.exists():
    raise RuntimeError(f"[{self.MODEL_ID}] venv not found at {python}. Run the extension's setup.py first.")

```

Unlike the CLI resolution, extension startup does not fall back to system Python. If the extension-specific virtual environment is missing, Modly raises a clear `RuntimeError` directing the user to run the extension's [`setup.py`](https://github.com/lightningpixel/modly/blob/main/setup.py).

## Platform-Specific Path Conventions

Modly handles platform differences automatically by checking `sys.platform` during path construction:

- **Windows**: Uses `Scripts/python.exe` structure
- **POSIX (Linux/macOS)**: Uses `bin/python` structure

This convention applies consistently across both the API server resolution in `_default_python` and the extension resolution in `_venv_python`, ensuring cross-platform compatibility without manual configuration.

## Practical Code Examples

### Starting the API Server with a Custom Interpreter

You can explicitly specify a Python interpreter when starting the server:

```bash

# Explicitly specify a Python interpreter

modly serve --python /opt/conda/envs/modly/bin/python

```

If omitted, Modly internally executes the resolution logic:

```python

# Inside tools/modly-cli/agent.py

python = Path(args.python).expanduser().resolve() if getattr(args, "python", None) else _default_python(api_dir)

```

### Running an Extension Process

When Modly spawns an extension, it uses the isolated venv path:

```python

# In api/services/extension_process.py

python = _venv_python(self.ext_dir)   # resolves to ext_dir/venv/.../python

subprocess.Popen([str(python), str(_RUNNER_PATH)], ...)

```

The extension's [`api/runner.py`](https://github.com/lightningpixel/modly/blob/main/api/runner.py) entrypoint receives execution through this specific interpreter path, maintaining complete isolation from the host Python environment.

## Summary

- **Modly** uses distinct resolution strategies for CLI server commands versus extension subprocesses.
- The CLI prioritizes: user-specified `--python`, API directory venv, Windows AppData venv, then `sys.executable`.
- Extensions require dedicated virtual environments and do not fall back to system Python if the venv is missing.
- Platform-specific path construction handles both Windows (`Scripts/python.exe`) and POSIX (`bin/python`) conventions automatically.
- Key implementation files are [`tools/modly-cli/agent.py`](https://github.com/lightningpixel/modly/blob/main/tools/modly-cli/agent.py) for CLI resolution and [`api/services/extension_process.py`](https://github.com/lightningpixel/modly/blob/main/api/services/extension_process.py) for extension isolation.

## Frequently Asked Questions

### What happens if no virtual environment is found when starting the Modly server?

If Modly cannot locate a virtual environment in the API directory or the Windows AppData fallback, it uses `sys.executable`—the Python interpreter that launched the CLI process. This ensures the server can still start using the system Python, though running within a dedicated virtual environment is recommended for dependency isolation.

### How do I specify a custom Python interpreter for the Modly server?

Pass the `--python` argument followed by the absolute path to your preferred interpreter. Modly immediately uses this path after expanding user variables and resolving symlinks via `Path(args.python).expanduser().resolve()`, bypassing the automatic venv detection entirely.

### Where does Modly look for extension virtual environments?

Modly expects extension virtual environments to reside at `{extension_directory}/venv/`. On Windows, it looks for `venv/Scripts/python.exe`; on POSIX systems, it looks for `venv/bin/python`. This path is constructed by the `_venv_python` helper function in [`api/services/extension_process.py`](https://github.com/lightningpixel/modly/blob/main/api/services/extension_process.py), which raises a `RuntimeError` if the directory does not exist.

### Does Modly support both Windows and Linux Python paths?

Yes. Modly automatically detects the operating system using `sys.platform` checks. For Windows platforms, it uses `Scripts/python.exe` paths; for Linux, macOS, and other POSIX systems, it uses `bin/python` paths. This logic is applied consistently across both the CLI server resolution and extension subprocess management.