/
niceSOFT
/
python3-setuptools_scm
Обзор
Документация
Войти
/
niceSOFT
/
python3-setuptools_scm
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
vcs-versioning/src/vcs_versioning/_worktree_discovery.py
170 строк
6 KB
Ronny Pfannschmidt
feat: restore Python 3.8 and 3.9 support
24 июн 2026, 11:12
24 июн 2026, 11:12
3546a0e
Код
Авторство
О чём код?
"""Workdir discovery: probe directories for SCM or fallback workdirs. ``discover_workdir`` uses a two-phase algorithm, calling registered factory entry points from the ``vcs_versioning.discover_workdir`` group: 1. **SCM phase** — probe ``absolute_root`` (and parent directories when ``search_parent_directories`` is enabled) for live VCS checkouts. 2. **Fallback phase** — if no SCM workdir was found, probe the project directory (where ``relative_to`` / pyproject.toml lives) for fallback workdirs only (archival files, egg-info metadata, etc.). SCM results are preferred over fallback results. """ from __future__ import annotations import logging from pathlib import Path from typing import Protocol, Union from ._backends._scm_workdir import ScmWorkdir from ._compat import entry_points from ._config import Configuration from ._fallback_workdir import FallbackWorkdir, StaticWorkdir log = logging.getLogger(__name__) AnyWorkdir = Union[ScmWorkdir, FallbackWorkdir] class DiscoveryFactory(Protocol): def __call__( self, path: Path, *, config: Configuration ) -> ScmWorkdir | FallbackWorkdir | None: ... def _verify_project_path(workdir: ScmWorkdir, config: Configuration) -> bool: """Check that the discovered project_path matches the configured one.""" if config.project_path is None: return True if workdir.project_path == config.project_path: return True log.warning( "project_path mismatch: config declares %r but discovery found %r", config.project_path, workdir.project_path, ) return False def _load_discovery_factories() -> list[tuple[str, DiscoveryFactory]]: """Load all factories from ``vcs_versioning.discover_workdir`` EPs.""" eps = entry_points(group="vcs_versioning.discover_workdir") result: list[tuple[str, DiscoveryFactory]] = [] for ep in eps: try: factory: DiscoveryFactory = ep.load() result.append((ep.name, factory)) except Exception: log.warning("failed to load discovery EP %s", ep.name, exc_info=True) return result def discover_workdir(config: Configuration) -> AnyWorkdir | None: """Discover the workdir for the given configuration. Algorithm: 0. If config.parse is set, return a LegacyParseWorkdir (deprecated path). 1. SCM phase: probe ``absolute_root`` (and parents when enabled). - ScmWorkdir result: verify project_path, return immediately. - FallbackWorkdir result: stash as candidate, keep probing for SCM. 2. Fallback phase: probe ``project_dir`` (if different from scm root). 3. Try each stashed FallbackWorkdir in discovery order; return the first whose ``get_scm_version()`` is not None. This prevents an unprocessed ``.git_archival.txt`` from shadowing a valid ``PKG-INFO`` (see :issue:`1431`). 4. Try StaticWorkdir from config.fallback_version / parentdir_prefix_version. 5. Return None. """ if config.parse is not None: from ._legacy_parse import LegacyParseWorkdir log.info("using LegacyParseWorkdir for config.parse (deprecated)") return LegacyParseWorkdir( path=Path(config.absolute_root), _config=config, parse_fn=config.parse, ) factories = _load_discovery_factories() if not factories: log.debug("no discovery factories registered") # Use the canonical path resolution from config. project_dir = config._resolved_paths.project_dir scm_root_hint = config._resolved_paths.scm_probe_root fallback_candidates: list[FallbackWorkdir] = [] def _accept_scm(result: ScmWorkdir, ep_name: str) -> ScmWorkdir: result.project_root = project_dir result._config = config if not _verify_project_path(result, config): raise ValueError( f"project_path mismatch: config declares " f"{config.project_path!r} but SCM root at {result.path} " f"yields {result.project_path!r}" ) log.info( "discovered SCM workdir %s (factory=%s)", type(result).__name__, ep_name ) return result def _probe_dir(current_dir: Path, *, accept_scm: bool) -> ScmWorkdir | None: for ep_name, factory in factories: try: result = factory(current_dir, config=config) except Exception: log.debug( "factory %s raised at %s", ep_name, current_dir, exc_info=True ) continue if result is None: continue if accept_scm and isinstance(result, ScmWorkdir): return _accept_scm(result, ep_name) if isinstance(result, FallbackWorkdir): result._config = config log.debug( "stashed fallback workdir %s from factory %s at %s", type(result).__name__, ep_name, current_dir, ) fallback_candidates.append(result) return None # Phase 1: SCM probes at scm_root_hint (the declared root) and optionally parents. scm_dirs: list[Path] = [scm_root_hint] if config.search_parent_directories: scm_dirs.extend(scm_root_hint.parents) for d in scm_dirs: scm = _probe_dir(d, accept_scm=True) if scm is not None: return scm # Phase 2: Fallback probes at project_dir (if different from scm_root_hint). if project_dir != scm_root_hint: _probe_dir(project_dir, accept_scm=False) # Try each fallback candidate until one can provide a version (#1431). # Earlier discovery code stashed all matching fallback workdirs; an # unprocessed .git_archival.txt (raw $Format placeholders) would # shadow a valid PKG-INFO if we only kept the first candidate. for candidate in fallback_candidates: if candidate.get_scm_version() is not None: log.info("using fallback workdir %s", type(candidate).__name__) return candidate if fallback_candidates: log.debug( "all %d fallback candidates returned None for get_scm_version", len(fallback_candidates), ) static = StaticWorkdir(path=project_dir, _config=config) if static.get_scm_version() is not None: log.info("using static fallback workdir") return static return None