Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ celerybeat.pid
# Environments
.env
.venv
.venv-analysis/
env/
venv/
ENV/
Expand Down
19 changes: 19 additions & 0 deletions docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ uv sync --locked --group automation --python 3.12

The second command selects an automation environment; use a separate UV_PROJECT_ENVIRONMENT when keeping application and tool environments side by side. `code2llm` itself is excluded from the registry catalog when it is the local editable package.

## Analysis environment (pfix / pipreqs)

The analyzer toolchain (`clickmd` / `code2logic` → `pfix` → `pipreqs`) enters the
locked dependency graph transitively. `pipreqs` 0.5.0 — the newest published
release — declares `Requires-Python >=3.8.1,<3.13`, so analyzer runs must not
use the application `.venv` on Python 3.13 (PLF-036 / issue #26). Until pipreqs
or pfix publishes a 3.13-compatible release, materialize the pinned analysis
environment instead:

```sh
scripts/analysis-env.sh # create/sync .venv-analysis (Python 3.12, uv.lock)
scripts/analysis-env.sh --check # + uv pip check + pipreqs smoke test
```

The application `.venv` is preserved untouched until the dependency-lock
review; `.venv-analysis` is disposable and rebuilds deterministically from
`uv.lock`. Run analyzer entry points with `.venv-analysis/bin/python`.


## Daily updates and verification

[Dependabot configuration](../.github/dependabot.yml) checks the explicit internal package allowlist daily, including weekends, and groups updates in one PR. It includes transitive dependencies and may widen a manifest constraint when needed. Local or Git sources require separate review.
Expand Down
47 changes: 47 additions & 0 deletions scripts/analysis-env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Pinned analysis environment for the analyzer toolchain (pfix -> pipreqs).
#
# pipreqs 0.5.0 (the newest published release) declares
# Requires-Python >=3.8.1,<3.13, so the analyzer chain must not run on the
# application .venv (Python 3.13) until pipreqs or pfix publishes a
# 3.13-compatible release. This script materializes a separate, locked
# Python 3.12 environment (PLF-036 / issue #26).
#
# Usage:
# scripts/analysis-env.sh # create/sync .venv-analysis
# scripts/analysis-env.sh --check # sync + uv pip check + smoke test
#
# The application .venv is never touched. Remove .venv-analysis only with
# `uv python dir` knowledge; it is disposable and rebuilds deterministically
# from uv.lock.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ANALYSIS_PYTHON="${ANALYSIS_PYTHON:-3.12}"
ANALYSIS_ENV="${ANALYSIS_ENV:-$ROOT/.venv-analysis}"

cd "$ROOT"
UV_PROJECT_ENVIRONMENT="$ANALYSIS_ENV" uv sync --locked --python "$ANALYSIS_PYTHON" --all-extras

if [[ "${1:-}" == "--check" ]]; then
UV_PROJECT_ENVIRONMENT="$ANALYSIS_ENV" uv pip check --python "$ANALYSIS_ENV/bin/python"
# Analyzer smoke test: pipreqs resolves imports for a small sample project.
"$ANALYSIS_ENV/bin/python" - <<'PY'
import tempfile
from pathlib import Path

from pipreqs import pipreqs

with tempfile.TemporaryDirectory() as tmp:
sample = Path(tmp)
(sample / "sample.py").write_text(
"import json\nimport pathlib\nimport yaml\n\nprint(json, pathlib, yaml)\n",
encoding="utf-8",
)
imports = pipreqs.get_all_imports(str(sample))
# pipreqs filters stdlib/local imports: only third-party names remain.
assert "yaml" in imports, f"third-party import not resolved: {imports}"
assert "json" not in imports, f"stdlib import should be filtered: {imports}"
print(f"analysis-env smoke OK (pipreqs resolved {len(imports)} imports)")
PY
fi
46 changes: 46 additions & 0 deletions tests/test_analysis_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Guardrails for the pinned Python 3.12 analysis environment (PLF-036 / #26).

The analyzer chain clickmd/code2logic -> pfix -> pipreqs cannot run on the
Python 3.13 application venv: pipreqs 0.5.0 declares Requires-Python
>=3.8.1,<3.13 and no newer release exists. These tests keep the analysis
environment script and its documentation discoverable and pinned.
"""
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "analysis-env.sh"
DOCS = ROOT / "docs" / "dependencies.md"


def test_analysis_env_script_exists_and_is_executable():
assert SCRIPT.is_file()
assert SCRIPT.stat().st_mode & 0o111, "analysis-env.sh must be executable"


def test_analysis_env_script_pins_python_312_and_locked_sync():
content = SCRIPT.read_text(encoding="utf-8")
assert 'ANALYSIS_PYTHON="${ANALYSIS_PYTHON:-3.12}"' in content
assert "uv sync --locked" in content
# The application venv must never be the target of the sync.
assert "UV_PROJECT_ENVIRONMENT" in content
assert "--all-extras" in content


@pytest.mark.parametrize(
("fragment",),
[
("pipreqs",),
(">=3.8.1,<3.13",),
("scripts/analysis-env.sh",),
(".venv-analysis",),
],
)
def test_dependency_docs_explain_the_analysis_environment(fragment):
assert fragment in DOCS.read_text(encoding="utf-8")


def test_gitignore_covers_analysis_environment():
ignores = (ROOT / ".gitignore").read_text(encoding="utf-8").splitlines()
assert ".venv-analysis/" in ignores
Loading