Skip to main content

Python virtual environment doctor — diagnose and fix broken venvs, pip, poetry locks

Project description

xnv — Python Virtual Environment Doctor

Diagnose and fix broken Python virtual environments, stale poetry locks, and pip issues.

Zero dependencies. Single file. Works when pip is broken.

python3 xnv.py fix   # repairs venv without needing pip

Table of Contents

Why?

Common Python environment failures:

Problem Cause xnv solution
pip: cannot execute: required file not found venv symlinks point to deleted/upgraded Python Detect broken symlinks → recreate venv
poetry.lock out of sync pyproject.toml edited without poetry lock Run poetry lock automatically
Conda + venv conflicts PATH confusion between conda base and project venv Detect CONDA_ACTIVE vs VIRTUAL_ENV mismatch
pyvenv.cfg stale home System Python relocated after venv creation Check pyvenv.cfg home dir exists
Broken pip shim pip binary broken but python -m pip works Reinstall pip via ensurepip

When pip is broken, you can't pip install a fix tool. xnv solves this by running as a single file with zero dependencies.

Quick Start (no install needed)

# 1. Download/copy xnv.py to your project
wget https://raw.githubusercontent.com/youruser/xnv/main/xnv.py
# or simply copy the file manually

# 2. Diagnose
python3 xnv.py                          # current directory
python3 xnv.py doctor /path/to/project  # specific project

# 3. Fix auto-magically
python3 xnv.py fix                      # diagnose + repair + install deps
python3 xnv.py fix --force              # force recreate venv
python3 xnv.py fix --no-install         # repair only, skip pip install

# 4. Manual operations
python3 xnv.py nuke                     # remove venv completely
python3 xnv.py create                   # create fresh venv
python3 xnv.py lock                     # regenerate poetry.lock

Installation (optional)

If you want the xnv shortcut command:

# Development install (editable)
pip install -e .

# Or use pipx for isolated install
pipx install .

# Then use `xnv` instead of `python3 xnv.py`
xnv doctor
xnv fix

Commands

doctor [path] — Diagnose environment

$ python3 xnv.py doctor

xnv doctor  /home/user/myproject

   [VENV_PYTHON_OK] venv Python: Python 3.12.4
   [VENV_PIP_OK] venv pip: pip 24.0
   [NO_PYPROJECT] No pyproject.toml found
   [POETRY_LOCK_STALE] poetry.lock out of sync with pyproject.toml
      hint: poetry lock

  1 errors / 1 warnings / 2 ok

✗ Issues found  run 'python3 xnv.py fix' to repair

fix [options] [path] — Auto-repair

Options:

  • --force, -f — Force recreate venv even if it appears healthy
  • --no-install — Skip dependency installation (repair only)
  • --dev — Install dev dependencies too (with Poetry)
# Standard repair
python3 xnv.py fix

# Force clean slate
python3 xnv.py fix --force

# Repair without installing packages
python3 xnv.py fix --no-install

What fix does:

  1. Runs full diagnosis
  2. If poetry.lock stale → poetry lock
  3. If venv broken → rm -rf venv && python3 -m venv venv
  4. Upgrades pip in new venv
  5. Installs dependencies (Poetry preferred, pip fallback)
  6. Verifies the fix

nuke [path] — Remove venv

python3 xnv.py nuke              # remove ./venv
python3 xnv.py nuke --name .venv # remove ./.venv

create [options] [path] — Create venv

python3 xnv.py create                    # create ./venv
python3 xnv.py create --name .venv       # create ./.venv
python3 xnv.py create --force            # recreate if exists

lock [path] — Fix poetry.lock

python3 xnv.py lock   # runs 'poetry lock'

Error Codes

Code Severity Description Auto-fixed by fix?
VENV_BROKEN_PYTHON error Python symlink points to missing target ✅ Yes
VENV_NO_PYTHON error Python binary missing from venv ✅ Yes
VENV_PYTHON_FAIL error Python binary not executable ✅ Yes
VENV_BROKEN_PIP error pip symlink broken ✅ Yes
VENV_NO_PIP error pip binary missing ✅ Yes (via ensurepip)
VENV_PIP_FAIL error pip not functional ✅ Yes
VENV_PIP_SHIM_BROKEN error pip shim broken but python -m pip works ✅ Yes (reinstall pip)
VENV_STALE_HOME error pyvenv.cfg home dir missing ✅ Yes
POETRY_LOCK_STALE error poetry.lock out of sync ✅ Yes
VENV_MISMATCH warn Active VIRTUAL_ENV ≠ project venv ❌ Manual fix
NO_VENV warn No venv found ✅ Yes (creates one)
NO_PYPROJECT warn No pyproject.toml ❌ N/A
POETRY_NO_LOCK warn poetry.lock missing ✅ Yes
CONDA_ACTIVE info Conda environment detected
VENV_EXTERNAL info Using external venv
VENV_PYTHON_OK info Python works
VENV_PIP_OK info pip works
POETRY_LOCK_OK info poetry.lock in sync

Troubleshooting

"pip: cannot execute: required file not found"

Symptom: /venv/bin/pip: cannot execute: required file not found

Cause: venv symlinks point to old Python location (e.g., after conda update)

Fix:

python3 xnv.py fix --force

poetry.lock keeps being stale

Symptom: POETRY_LOCK_STALE keeps appearing

Cause: pyproject.toml dependencies changed without updating lock

Fix:

python3 xnv.py lock      # regenerate lock
python3 xnv.py fix       # reinstall with new lock

Conda base env active

Symptom: CONDA_ACTIVE shows (base) even though you want project venv

Fix:

conda deactivate
python3 xnv.py fix
source venv/bin/activate

Venv exists but xnv says "NO_VENV"

Symptom: venv/ exists but xnv doesn't detect it

Check: Does venv/pyvenv.cfg exist? If not, it's not a proper venv.

Fix:

python3 xnv.py fix --force

Architecture

Two modes of operation

┌─────────────────┐     ┌─────────────────┐
│  Standalone     │     │  Package        │
│  xnv.py         │     │  pip install -e │
│                 │     │                 │
│  • Zero deps    │     │  • CLI shortcut │
│  • Copy anywhere│     │  • Importable   │
│  • Works when   │     │    API          │
│    pip broken   │     │                 │
└─────────────────┘     └─────────────────┘
         │                       │
         └───────────┬───────────┘
                     │
              ┌──────▼──────┐
              │  Core logic  │
              │  (diagnose, │
              │   fix)       │
              └─────────────┘

Detection logic

def diagnose(project_dir) -> Diagnosis:
    1. Find venv (venv, .venv, env, .env)
    2. Check Python binary exists & is executable
    3. Check pip binary exists & works
    4. Check pyvenv.cfg home dir valid
    5. Check poetry.lock sync (if poetry available)
    6. Check conda vs venv conflicts
    7. Return list of Issues

Makefile Integration

Add to your project's Makefile:

# Use standalone xnv.py (no install needed)
fix-env:
	@python3 $(XNV_PATH)/xnv.py fix .

doctor-env:
	@python3 $(XNV_PATH)/xnv.py doctor .

# Or if xnv installed
fix-env:
	@xnv fix .

Python API

For programmatic use after pip install -e .:

from xnv.diagnose import diagnose, Issue
from xnv.fix import fix

# Diagnose
 diag = diagnose("/path/to/project")
print(f"Healthy: {diag.healthy}")
print(f"Venv: {diag.venv_path}")

for issue in diag.issues:
    print(f"[{issue.severity}] {issue.code}: {issue.message}")
    if issue.fix_hint:
        print(f"  Hint: {issue.fix_hint}")

# Fix
ok = fix("/path/to/project", force_recreate=False, install=True, dev=False)
if ok:
    print("Environment fixed!")
else:
    print("Some issues remain")

Data classes

@dataclass
class Issue:
    code: str           # e.g., "VENV_BROKEN_PYTHON"
    severity: str       # "error", "warn", "info"
    message: str        # Human-readable description
    fix_hint: str       # Suggested fix command

@dataclass
class Diagnosis:
    project_dir: Path
    venv_path: Path | None
    python_path: Path | None
    pip_path: Path | None
    poetry_available: bool
    poetry_lock_stale: bool
    conda_active: bool
    issues: list[Issue]
    
    @property
    def healthy(self) -> bool: ...
    @property
    def errors(self) -> list[Issue]: ...
    @property
    def warnings(self) -> list[Issue]: ...

How it works (single file)

xnv.py is a self-contained Python script that:

  1. Uses only stdlib (argparse, dataclasses, pathlib, subprocess, etc.)
  2. Implements all diagnosis/fix logic inline
  3. Has zero external dependencies
  4. Can be copied to any project and run immediately

This is the bootstrap problem solution: when your package manager (pip/poetry) is broken, you can't use it to install a fix tool. xnv bypasses this by not needing installation.

Contributing

  1. Fork and clone
  2. pip install -e ".[dev]"
  3. python3 -m pytest tests/ -v
  4. Make changes, add tests
  5. Submit PR

License

Apache License 2.0 - see LICENSE for details.

Author

Created by Tom Sapletta - tom@sapletta.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xnv-0.1.2.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xnv-0.1.2-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file xnv-0.1.2.tar.gz.

File metadata

  • Download URL: xnv-0.1.2.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for xnv-0.1.2.tar.gz
Algorithm Hash digest
SHA256 30500768aaedc510703a55f4e9273398a1b817b5e699bd2d0d744367b859f09f
MD5 9100d186950803dd8362b2b5e2fb16f9
BLAKE2b-256 5f622215e1e90705aa561e622ea8d84aec1667100919dd9d011f40497132395d

See more details on using hashes here.

File details

Details for the file xnv-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: xnv-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for xnv-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 15453bb0e5df2440fc8c72ccbde56f8f7255bc9737f9ac1ce77e90b2401ab48f
MD5 e84418ffab4bdc7c9eeafacbe1d8007e
BLAKE2b-256 e5d5ce90e42fc92181ce6b3d63bd71c863638000e7affe71c3925dbf6b9a9b61

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page