mcilspy/src/mcilspy/utils.py
Ryan Malloy 8901752ae3 refactor: consolidate architecture across 8 issues (A1-A8)
- A1: Extract duplicated PATH discovery to utils.py (single source of truth)
- A2: Convert metadata_reader dataclasses to Pydantic models in models.py
- A3: Simplify get_wrapper() with module-level caching (removed fragile lifespan context)
- A4: Document ILSpyWrapper design rationale (why class exists despite being stateless)
- A5: Document MetadataReader as CPU-bound sync code with thread pool suggestion
- A6: Create constants.py for all timeouts/limits (DECOMPILE_TIMEOUT_SECONDS, etc.)
- A7: Add _compile_search_pattern() helper to deduplicate regex compilation
- A8: Add LanguageVersion validation with helpful error listing valid options

Tests pass, ruff clean.
2026-02-08 11:25:43 -07:00

51 lines
1.6 KiB
Python

"""Shared utility functions for mcilspy.
This module contains common utilities used across the codebase to avoid
code duplication and ensure consistent behavior.
"""
import logging
import os
import shutil
logger = logging.getLogger(__name__)
def find_ilspycmd_path() -> str | None:
"""Find ilspycmd executable in PATH or common install locations.
This is the single source of truth for locating the ilspycmd binary.
It checks:
1. Standard PATH (via shutil.which)
2. ~/.dotnet/tools (default location for 'dotnet tool install --global')
3. Platform-specific locations (Windows %USERPROFILE%)
Returns:
Path to ilspycmd executable if found, None otherwise
"""
# Check PATH first (handles both ilspycmd and ilspycmd.exe)
for cmd_name in ["ilspycmd", "ilspycmd.exe"]:
path = shutil.which(cmd_name)
if path:
return path
# Check common dotnet tools locations (not always in PATH for MCP servers)
home = os.path.expanduser("~")
candidates = [
os.path.join(home, ".dotnet", "tools", "ilspycmd"),
os.path.join(home, ".dotnet", "tools", "ilspycmd.exe"),
]
# Windows-specific: also check USERPROFILE if different from ~
if os.name == "nt":
userprofile = os.environ.get("USERPROFILE", "")
if userprofile and userprofile != home:
candidates.append(os.path.join(userprofile, ".dotnet", "tools", "ilspycmd.exe"))
for candidate in candidates:
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
logger.info(f"Found ilspycmd at {candidate} (not in PATH)")
return candidate
return None