refactor: workdir-centric chain API (env → config → workdir → version)#1378
Open
RonnyPfannschmidt wants to merge 15 commits into
Open
refactor: workdir-centric chain API (env → config → workdir → version)#1378RonnyPfannschmidt wants to merge 15 commits into
RonnyPfannschmidt wants to merge 15 commits into
Conversation
Introduces the workdir-centric API as the primary version inference path, replacing the parse-based entry point mechanism for built-in SCM handling. Changes: - Evolve Workdir into ScmWorkdir with project_root/project_path - Add FallbackWorkdir hierarchy (Metadata, Archived, PkgInfo, Static) - Implement discover_workdir() with smart probe factories - Add vcs_versioning.discover_workdir EP group - Remove legacy parse_scm/parse_scm_fallback EPs from vcs-versioning - Move PKG-INFO discovery to setuptools-scm (setuptools artifact) - Add project_path to Configuration (pypa#872) with root bridge - Add per-project overrides via .config/python-vcs-versioning.toml - Write scm_version.json + scm_file_list.json to egg-info for sdists Still to address: - Consolidate old parse() functions into workdir get_scm_version() (currently the workdir methods wrap the old parse internals) - Backward compat testing with mock third-party parse plugins - API surface assessment: document public vs internal boundaries and migration path for third-party integrators - File finder integration: active workdir only used as fallback after legacy file finder EPs; needs full validation - Deprecation warnings for root → project_path migration not yet enabled (bridge silently computes) Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude <claude@anthropic.com>
…xtVar Restructure _get_version_impl.py to reflect the clean workdir-centric pipeline (Config -> Workdir -> ScmVersion -> formatted version): - Extract legacy EP dispatch to _legacy_parse.py (parse_scm_version, parse_fallback_version, has_legacy_parse_eps, resolved_fallback_root) - Delete parse_version() orchestrator; replace with _resolve_version() that inlines the pipeline directly - Add _finalize() helper to avoid repeating overrides+format+write - Inline the pipeline in setuptools-scm's infer_version_with_config, giving it direct access to the discovered workdir - Add workdir field to VersionInferenceData so downstream consumers (egg_info mixin, file finders) can access it via Distribution - Remove ContextVar (_active_workdir), get_active_workdir, and set_active_workdir entirely -- no more global mutable state - Remove ContextVar fallback from find_files; file finding now relies solely on registered entry points (workdir-based finding will come via setuptools egg_info mixin) - Update create_release_proposal.py to use discover_workdir directly - Update test monkeypatch target from parse_version to _resolve_version Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude <claude@anthropic.com>
Replace the fragile _write_scm_metadata_to_egg_info (which guessed the egg-info path and silently skipped on clean builds) with a proper egg_info command mixin that: - Overrides find_sources() to supply tracked files from the discovered workdir directly to manifest_maker, bypassing the walk_revctrl() -> setuptools.file_finders EP chain that cannot pass context. - Writes scm_version.json and scm_file_list.json into the egg-info directory in run(), where the directory is guaranteed to exist. Follows the same registration pattern as ScmVersionFileMixin/build_py. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude <claude@anthropic.com>
Add integration test that a git-tracked file excluded via MANIFEST.in is absent from the sdist, even when the egg_info mixin supplies tracked files directly (bypassing walk_revctrl). Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude <claude@anthropic.com>
Extend the MANIFEST.in integration test with two phases: 1. Empty MANIFEST.in → git-tracked secret.txt IS in the sdist 2. MANIFEST.in with "exclude secret.txt" → file is absent Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude <claude@anthropic.com>
Store Configuration on workdirs via _config field so get_scm_version() and run_describe() no longer require an explicit config parameter. Introduce VcsEnvironment to capture runtime settings (timeout, hg command, SOURCE_DATE_EPOCH) without ContextVar. Add instance run_git/run_hg methods that thread env settings. Backward-compat shims in setuptools-scm re-export modules accept optional config arg. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Opus 4 <claude@anthropic.com>
Remove get_active_overrides(), get_debug_level(), get_subprocess_timeout(), get_hg_command(), get_source_date_epoch(), and source_epoch_or_utc_now() module-level accessor functions from vcs_versioning.overrides. Each call site now reads configuration explicitly: - _version_missing() accepts a `tool` parameter instead of querying globals - is_toplevel_acceptable() accepts `ignore_vcs_roots` or reads os.environ - _get_timeout() reads SUBPROCESS_TIMEOUT from env with prefix fallback - _get_hg_command() reads HG_COMMAND from env with prefix fallback - _source_epoch_or_utc_now() reads SOURCE_DATE_EPOCH from os.environ - _read_pretended_*() and read_toml_overrides() accept explicit `env` GlobalOverrides, GlobalOverrides.from_env(), and ensure_context() are retained as the public API for entering override contexts. External consumer research (grep.app + GitHub code search): - scikit-build/scikit-build-core: uses GlobalOverrides.from_env() only (safe) - nipreps/version-schemes: uses GlobalOverrides.from_env() only (safe) - kulgan/setuptools-scmx: uses GlobalOverrides.from_env() only (safe) - No external project imports or calls any of the removed accessor functions Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Opus 4 <claude@anthropic.com>
Configuration.env is now always non-None — __post_init__ falls back to VcsEnvironment.from_env() when no explicit environment was provided. This eliminates all None-checks downstream and ensures runtime settings (timeout, hg_command, source_date_epoch) flow through the explicit chain: VcsEnvironment -> Configuration -> workdir -> version. Key changes: - Configuration.__post_init__ creates VcsEnvironment.from_env() fallback - Configuration.env property provides typed access (always non-None) - ScmWorkdir._subprocess_timeout/._hg_command use config.env directly - _scm_version.meta() unconditionally reads config.env.source_date_epoch - _should_write_to_source() receives config instead of standalone EnvReader - LegacyParseWorkdir wraps config.parse in discover_workdir uniformly - _compat_helpers simplified (env always present, no manual fallback) - docs/integrators.md documents as breaking change with migration path Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Opus 4 <claude@anthropic.com>
Override helpers (_read_pretended_version_for, _read_pretended_metadata_for)
now derive tool names from config.env.tool_names instead of hardcoding
("SETUPTOOLS_SCM", "VCS_VERSIONING"). This ensures other tools using
vcs-versioning get their own prefixes respected.
To support this, the setuptools-scm layer passes VcsEnvironment with
SETUPTOOLS_SCM prefix through the chain:
- setuptools_scm.get_version() creates VcsEnvironment.from_env("SETUPTOOLS_SCM")
- get_version() accepts _env parameter, forwarded to Configuration
- WorkDir._env allows test fixtures to inject the right env
- setuptools-scm conftest sets wd._env with SETUPTOOLS_SCM prefix
Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Anthropic Claude Opus 4 <claude@anthropic.com>
VcsEnvironment is now the single source of truth for all env-var settings (timeout, hg command, epoch, ignore roots, debug). GlobalOverrides becomes a thin wrapper that delegates to vcs_env for runtime values and adds context-manager semantics for logging configuration. - Add debug field, _env mapping, log_level(), and make_reader() to VcsEnvironment - Convert GlobalOverrides from frozen dataclass to plain class with __slots__ - from_env() delegates entirely to VcsEnvironment.from_env() -- zero duplicated parsing - from_active() supports overriding VcsEnvironment fields via dataclasses.replace() - Backward-compatible properties (debug, subprocess_timeout, etc.) delegate to vcs_env Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Opus 4 <claude@anthropic.com>
…resolution Replace aliased class imports (e.g. VcsEnvironment as _VcsEnvironment) under TYPE_CHECKING with module imports, then use module.Class in annotations. Combined with from __future__ import annotations, this gives proper type checking without Any escape hatches or runtime imports. Also fix missing assert statements in test_nested_from_active_contexts. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Opus 4 <claude@anthropic.com>
VcsEnvironment now stores additional_loggers and provides configure_logging() which sets up all loggers at the correct level. GlobalOverrides.__enter__ delegates to vcs_env.configure_logging(). - Add additional_loggers field to VcsEnvironment - Add configure_logging() method to VcsEnvironment - Remove additional_loggers from GlobalOverrides __slots__/__init__ - Add backward-compat property delegating to vcs_env - from_env() attaches loggers via dataclasses.replace - Simplify _log._get_all_scm_loggers: no longer reads _active_overrides Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Opus 4 <claude@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors version inference to a workdir-centric, explicitly-threaded pipeline (VcsEnvironment -> Configuration -> workdir -> ScmVersion -> formatted string), replacing prior reliance on implicit/global state. It also introduces workdir discovery entry points and setuptools integration that can persist SCM metadata into egg-info for sdist fallbacks.
Changes:
- Add
VcsEnvironmentand thread runtime settings through config/workdir/version instead of magic globals/ContextVars. - Replace parse-entry-point discovery with
discover_workdir()+ workdir objects (SCM + fallback workdirs), including JSON metadata read/write helpers. - Add setuptools-scm
egg_infomixin + discovery factories to source tracked files from the discovered workdir and to write/read SCM metadata inegg-info.
Reviewed changes
Copilot reviewed 52 out of 52 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/integrators.md | Documents chained API + migration guidance for integrators. |
| docs/overrides.md | Updates override priority and resolution pipeline documentation. |
| setuptools-scm/changelog.d/compat-shims.feature.md | Notes compatibility shims for legacy workdir APIs. |
| setuptools-scm/pyproject.toml | Registers vcs_versioning.discover_workdir EPs for setuptools-scm fallbacks. |
| setuptools-scm/src/setuptools_scm/_compat_helpers.py | Adds internal helper for temporarily binding config to workdirs. |
| setuptools-scm/src/setuptools_scm/_get_version.py | Creates VcsEnvironment with SETUPTOOLS_SCM prefix and forwards into core get_version. |
| setuptools-scm/src/setuptools_scm/_integration/_discover.py | Adds setuptools-scm discovery factories (PKG-INFO, egg-info metadata). |
| setuptools-scm/src/setuptools_scm/_integration/build_py.py | Extends inference data to carry the discovered workdir. |
| setuptools-scm/src/setuptools_scm/_integration/egg_info.py | Adds egg_info mixin to use workdir-tracked files and write SCM metadata into egg-info. |
| setuptools-scm/src/setuptools_scm/_integration/setuptools.py | Registers the new egg_info mixin command in setuptools integration. |
| setuptools-scm/src/setuptools_scm/_integration/version_inference.py | Inlines the version pipeline and stores workdir in inference data. |
| setuptools-scm/src/setuptools_scm/git.py | Adds backward-compatible shim GitWorkdir accepting optional config. |
| setuptools-scm/src/setuptools_scm/hg.py | Adds backward-compatible shim HgWorkdir accepting optional config. |
| setuptools-scm/src/setuptools_scm/hg_git.py | Adds backward-compatible shim GitWorkdirHgClient accepting optional config. |
| setuptools-scm/src/setuptools_scm/scm_workdir.py | Adds backward-compatible shim ScmWorkdir accepting optional config. |
| setuptools-scm/testing_scm/conftest.py | Ensures tests attach a SETUPTOOLS_SCM-prefixed VcsEnvironment to WorkDir. |
| setuptools-scm/testing_scm/test_basic_api.py | Updates monkeypatch target to _resolve_version. |
| setuptools-scm/testing_scm/test_integration.py | Adds MANIFEST.in exclusion integration test for sdist with egg_info mixin. |
| setuptools-scm/testing_scm/test_sdist_metadata.py | Adds tests for egg-info metadata discovery and JSON round-tripping. |
| src/vcs_versioning_workspace/create_release_proposal.py | Switches release proposal script to discover_workdir() workflow. |
| vcs-versioning/changelog.d/workdir-config-error.bugfix.md | Notes improved error for unbound workdir config access. |
| vcs-versioning/pyproject.toml | Replaces parse EPs with vcs_versioning.discover_workdir EPs. |
| vcs-versioning/src/vcs_versioning/init.py | Exposes VcsEnvironment in public API. |
| vcs-versioning/src/vcs_versioning/_backends/_discover_vcs.py | Adds smart-probe factory for git/hg/hg-git discovery. |
| vcs-versioning/src/vcs_versioning/_backends/_git.py | Adds instance methods + workdir API methods (version, file listing, tracking). |
| vcs-versioning/src/vcs_versioning/_backends/_hg.py | Adds instance methods + workdir API methods (version, file listing, tracking). |
| vcs-versioning/src/vcs_versioning/_backends/_hg_git.py | Adds workdir API methods for hg-git hybrid. |
| vcs-versioning/src/vcs_versioning/_backends/_scm_workdir.py | Refactors base SCM workdir type to hold project_root + config backref. |
| vcs-versioning/src/vcs_versioning/_cli/init.py | Threads VcsEnvironment through CLI config creation. |
| vcs-versioning/src/vcs_versioning/_config.py | Adds project_path, stores _env, and provides config.discover_workdir(). |
| vcs-versioning/src/vcs_versioning/_environment.py | Introduces VcsEnvironment for runtime settings and config construction. |
| vcs-versioning/src/vcs_versioning/_fallback_workdir.py | Adds fallback workdir hierarchy (archival, PKG-INFO, static, metadata). |
| vcs-versioning/src/vcs_versioning/_file_finders/init.py | Adjusts ignore-roots handling and keeps EP-based file finder dispatch. |
| vcs-versioning/src/vcs_versioning/_get_version_impl.py | Refactors to _resolve_version() + _finalize() and uses workdir discovery. |
| vcs-versioning/src/vcs_versioning/_legacy_parse.py | Extracts legacy parse EP dispatch and wraps config.parse as a workdir. |
| vcs-versioning/src/vcs_versioning/_log.py | Removes dependency on active overrides for logger discovery. |
| vcs-versioning/src/vcs_versioning/_overrides.py | Reads pretend overrides using config.env.tool_names and allows env injection. |
| vcs-versioning/src/vcs_versioning/_project_overrides.py | Adds (currently unused) per-project override reader. |
| vcs-versioning/src/vcs_versioning/_run_cmd.py | Reads timeout env vars directly for standalone callers. |
| vcs-versioning/src/vcs_versioning/_scm_metadata.py | Adds JSON read/write helpers for SCM version + tracked file lists. |
| vcs-versioning/src/vcs_versioning/_scm_version.py | Adds ScmVersion.format() and updates time derivation from SOURCE_DATE_EPOCH. |
| vcs-versioning/src/vcs_versioning/_test_utils.py | Allows tests to inject an explicit environment into get_version(). |
| vcs-versioning/src/vcs_versioning/_worktree_discovery.py | Adds discover_workdir() entry-point based discovery pipeline. |
| vcs-versioning/testing_vcs/test_chain_api.py | Adds tests for the chained env->config->workdir->version API. |
| vcs-versioning/testing_vcs/test_git.py | Updates SOURCE_DATE_EPOCH handling in dirty-tag test. |
| vcs-versioning/testing_vcs/test_overrides_api.py | Updates tests to use _active_overrides ContextVar directly. |
| vcs-versioning/testing_vcs/test_project_overrides.py | Adds tests for per-project overrides file reader. |
| vcs-versioning/testing_vcs/test_project_path.py | Adds tests for project_path and root->project_path bridging. |
| vcs-versioning/testing_vcs/test_scm_metadata.py | Adds tests for SCM JSON metadata round-trip. |
| vcs-versioning/testing_vcs/test_workdir_api.py | Adds tests for workdir method APIs and fallbacks. |
| vcs-versioning/testing_vcs/test_workdir_discovery.py | Adds tests for discovery ordering, project_path verification, and factories. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Implement lazy config.env with DeprecationWarning, resolve_runtime_env for GlobalOverrides bridges, thread hg settings through workdirs, harden metadata parsing, normalize sdist file lists, and thread tool_names into TOML override reads. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Sonnet 4.5 <claude@anthropic.com>
…sion Use forward-slash project_path on all platforms for stable comparisons, and attach resolve_runtime_env() in get_version() without leaking imports into Configuration kwargs (fixes Windows CI and mercurial warning test). Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Anthropic Claude Sonnet 4.5 <claude@anthropic.com>
xmlsec excludes PyPy in its cibuildwheel config; the test passes on CPython. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Composer <composer@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
VcsEnvironmentas the explicit entry point for runtime settings (subprocess timeout, hg command,SOURCE_DATE_EPOCH, debug, env-var prefixes) and wires them throughConfiguration→ workdir →ScmVersioninstead of magic globals /ContextVar.discover_workdir()and workdir objects (ScmWorkdir, fallbacks, egg-info metadata); setuptools-scm gains anegg_infomixin for workdir-based file finding and SCM metadata in sdist/egg-info.GlobalOverridesto wrapVcsEnvironment(logging +EnvReader); setuptools hooks andget_versionuseensure_context("SETUPTOOLS_SCM"). Backward-compatible shims remain forsetuptools_scm.git/hg/scm_workdir.Test plan
uv run pytest vcs-versioning/testing_vcs -n12uv run pytest setuptools-scm/testing_scm -n12uv run pytest -n12(full suite)pre-commit run --all-filesMade with Cursor