From a70482fdf1e550e5cb9fcfcf42e0ad55aa7494f2 Mon Sep 17 00:00:00 2001 From: Nose Date: Fri, 9 Jan 2026 15:41:38 -0800 Subject: [PATCH] k --- scripts/bootstrap.py | 29 +++++++++++++++++++------ scripts/cli_entry.py | 50 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/scripts/bootstrap.py b/scripts/bootstrap.py index 179a3ae..16d8675 100644 --- a/scripts/bootstrap.py +++ b/scripts/bootstrap.py @@ -302,6 +302,11 @@ def main() -> int: # This prevents issues when bootstrap.py is run from different directories script_dir = Path(__file__).resolve().parent repo_root = script_dir.parent + + if not args.quiet: + print(f"Bootstrap script location: {script_dir}") + print(f"Detected project root: {repo_root}") + print(f"Current working directory: {Path.cwd()}") # Helpers for interactive menu and uninstall detection def _venv_python_path(p: Path) -> Path | None: @@ -554,6 +559,14 @@ def main() -> int: # Opinionated: always create or use a local venv at the project root (.venv) venv_dir = repo_root / ".venv" + + # Validate that venv_dir is where we expect it to be + if not args.quiet: + print(f"Planned venv location: {venv_dir}") + if venv_dir.parent != repo_root: + print(f"WARNING: venv parent is {venv_dir.parent}, expected {repo_root}", file=sys.stderr) + if "scripts" in str(venv_dir).lower(): + print(f"WARNING: venv path contains 'scripts': {venv_dir}", file=sys.stderr) def _venv_python(p: Path) -> Path: if platform.system().lower() == "windows": @@ -834,22 +847,25 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { user_bin = Path(os.environ.get("USERPROFILE", str(home))) / "bin" user_bin.mkdir(parents=True, exist_ok=True) + # Convert repo path to string with proper escaping + repo_str = str(repo).replace("\\", "\\\\") + # Write mm.ps1 (PowerShell shim) mm_ps1 = user_bin / "mm.ps1" ps1_text = ( "Param([Parameter(ValueFromRemainingArguments=$true)] $args)\n" - f'$repo = "{repo}"\n' + f'$repo = "{repo_str}"\n' "$venv = Join-Path $repo '.venv'\n" "$py = Join-Path $venv 'Scripts\\python.exe'\n" "if (Test-Path $py) {\n" " if ($env:MM_DEBUG) {\n" ' Write-Host "MM_DEBUG: using venv python at $py" -ForegroundColor Yellow\n' - " & $py -c \"import sys; print('sys.executable:', sys.executable)\"\n" + " & $py -c \"import sys; print('sys.executable:', sys.executable); print('sys.path:', sys.path[:5])\"\n" " }\n" " & $py -m scripts.cli_entry @args; exit $LASTEXITCODE\n" "}\n" "# Fallback to system python if venv doesn't exist\n" - "if ($env:MM_DEBUG) { Write-Host 'MM_DEBUG: venv python not found, trying system python' -ForegroundColor Yellow }\n" + "if ($env:MM_DEBUG) { Write-Host 'MM_DEBUG: venv python not found at' $py ', trying system python' -ForegroundColor Yellow }\n" "python -m scripts.cli_entry @args\n" ) if mm_ps1.exists(): @@ -859,21 +875,22 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { # Write mm.bat (CMD shim for better compatibility) mm_bat = user_bin / "mm.bat" + repo_bat_str = str(repo) bat_text = ( "@echo off\n" "setlocal enabledelayedexpansion\n" - f'set "REPO={repo}"\n' + f'set "REPO={repo_bat_str}"\n' "set \"VENV=!REPO!\\.venv\"\n" "set \"PY=!VENV!\\Scripts\\python.exe\"\n" "if exist \"!PY!\" (\n" " if defined MM_DEBUG (\n" " echo MM_DEBUG: using venv python at !PY!\n" - " \"!PY!\" -c \"import sys; print('sys.executable:', sys.executable)\"\n" + " \"!PY!\" -c \"import sys; print('sys.executable:', sys.executable); print('sys.path:', sys.path[:5])\"\n" " )\n" " \"!PY!\" -m scripts.cli_entry %*\n" " exit /b !ERRORLEVEL!\n" ")\n" - "echo MM: venv not found at !VENV!, trying system python\n" + "echo MM: venv python not found at !PY!\n" "if defined MM_DEBUG echo MM_DEBUG: venv python not found, trying system python\n" "python -m scripts.cli_entry %*\n" "exit /b !ERRORLEVEL!\n" diff --git a/scripts/cli_entry.py b/scripts/cli_entry.py index bd9c160..ebb7ab2 100644 --- a/scripts/cli_entry.py +++ b/scripts/cli_entry.py @@ -11,6 +11,7 @@ from __future__ import annotations from typing import Optional, List, Tuple import importlib import importlib.util +import os import sys from pathlib import Path import shlex @@ -25,7 +26,7 @@ def _ensure_repo_root_on_sys_path(pkg_file: Optional[Path] = None) -> Optional[P not necessarily on `sys.path`, which breaks `import CLI`. We infer the repo root by walking up from this package location and looking - for a sibling `CLI.py`. + for a sibling `CLI.py` or checking parent directories. `pkg_file` exists for unit tests; production uses this module's `__file__`. """ @@ -34,6 +35,7 @@ def _ensure_repo_root_on_sys_path(pkg_file: Optional[Path] = None) -> Optional[P except Exception: return + # Strategy 1: Look for CLI.py in parent directories (starting from scripts parent) for parent in pkg_dir.parents: try: if (parent / "CLI.py").exists(): @@ -43,6 +45,22 @@ def _ensure_repo_root_on_sys_path(pkg_file: Optional[Path] = None) -> Optional[P return parent except Exception: continue + + # Strategy 2: If in a venv, check the .venv/.. path (project root) + try: + # If this file is in .../venv/lib/python3.x/site-packages/scripts/ + # then we want to go up to find the project root + current = pkg_dir.resolve() + for _ in range(20): # Safety limit + current = current.parent + if (current / "CLI.py").exists(): + parent_str = str(current) + if parent_str not in sys.path: + sys.path.insert(0, parent_str) + return current + except Exception: + pass + return None @@ -165,13 +183,35 @@ def _run_cli(clean_args: List[str]) -> int: # 2) If no in-memory module provided the class, try importing the repo-root CLI if MedeiaCLI is None: try: - _ensure_repo_root_on_sys_path() + repo_root = _ensure_repo_root_on_sys_path() from CLI import MedeiaCLI as _M # type: ignore MedeiaCLI = _M - except Exception: - raise ImportError( - "Could not import 'MedeiaCLI'. This often means the project is not available on sys.path (run 'pip install -e scripts' or re-run the bootstrap script)." + except Exception as exc: + # Provide diagnostic information + import traceback + error_msg = ( + "Could not import 'MedeiaCLI'. This often means the project is not available on sys.path.\n" + "Diagnostic info:\n" + f" - sys.executable: {sys.executable}\n" + f" - sys.path (first 5): {sys.path[:5]}\n" + f" - current working directory: {Path.cwd()}\n" + f" - this file: {Path(__file__).resolve()}\n" ) + try: + repo = _ensure_repo_root_on_sys_path() + if repo: + error_msg += f" - detected repo root: {repo}\n" + cli_path = repo / "CLI.py" + error_msg += f" - CLI.py exists at {cli_path}: {cli_path.exists()}\n" + except: + pass + error_msg += ( + "\nRemedy: Run 'pip install -e scripts' from the project root or re-run the bootstrap script.\n" + "Set MM_DEBUG=1 to enable detailed diagnostics." + ) + if os.environ.get("MM_DEBUG"): + error_msg += f"\n\nTraceback:\n{traceback.format_exc()}" + raise ImportError(error_msg) from exc try: app = MedeiaCLI()