Skip to content

Maintenance/code cleanup 2026-05 Phase II (venv locator)#6665

Open
matteius wants to merge 18 commits into
maintenance/code-cleanup-2026-05from
maintenance/code-cleanup-phase2-2026-05
Open

Maintenance/code cleanup 2026-05 Phase II (venv locator)#6665
matteius wants to merge 18 commits into
maintenance/code-cleanup-2026-05from
maintenance/code-cleanup-phase2-2026-05

Conversation

@matteius
Copy link
Copy Markdown
Member

@matteius matteius commented May 12, 2026

Summary

Phase 2 of the pipenv maintenance-mode modernization stack. Bases on top of PR #6663 (Phase 1). Phase 3 and Phase 4 land as their own follow-up PRs (links below).

14 commits, +2602 / −514 across pipenv/ and tests/, plus three new design docs under docs/dev/. Touches zero vendored or patched files. 679 unit tests pass (was 651 baseline at start of Phase 2 = +28 new).

In this PR

T_D.4 — VenvLocator subsystem extraction (Initiative D, continued)

  • NEW pipenv/utils/venv_locator.py (431 lines) holds the 13 venv-classified methods/properties extracted from Project.
  • pipenv/project.py: 1526 → 1281 lines (-245 net). Project.venv_locator is a @cached_property returning a VenvLocator(self) instance.
  • API renamed to drop the now-redundant virtualenv_ prefix: project.virtualenv_locationproject.venv_locator.location, project.virtualenv_nameproject.venv_locator.name, etc. (same Sources/Settings cascade pattern as T_D.2 / T_D.3 in PR Maintenance/code cleanup 2026-05 Phase I (general base) #6663).
  • ~38 internal caller sites migrated in the same commit per the "CLI is the only stable Python API" sign-off — no deprecation shims.
  • 13 new unit tests in tests/unit/test_venv_locator.py.

T_E.3 — Continued requirement-model consolidation (Initiative E)

Four helpers moved from pipenv/utils/requirementslib.py (the legacy-vendored module) to pipenv/utils/dependencies.py (the canonical home):

  • is_vcs
  • add_ssh_scheme_to_git_uri
  • merge_items (+ private helpers _merge_into / _new_container_like)
  • get_pip_command

requirementslib.py shrinks 275 → 144 lines (-131). After this PR it holds only the unpack_url/get_http_url fork pair + VCS_SCHEMES, which T_E.4 (queued) will move next.

T_F.2 — Typed resolver subprocess protocol DESIGN (Initiative F design phase)

Three new design docs (no code changes for the resolver itself yet — execution is on the phase-3 branch, see below):

  • NEW docs/dev/initiative-f-typed-design.md (886 lines) — proposes the typed ResolverRequest / ResolverResponse envelope, the discriminated ResolverResult union, the canonical LockedRequirement.from_install_requirement formatter that will replace both Entry.get_cleaned_dict and format_requirement_for_lockfile, the §6a "future pluggable resolver backends" addendum, and the §3.6 target-Python execution constraint (the schema module must work under the target venv's Python, not the parent pipenv's). Resolves the 11 deferred decisions from F.1 and surfaces 10 sign-off questions for the maintainer.
  • NEW docs/dev/initiative-f-execution-plan.md (359 lines) — the swarm-ready dependency-aware execution plan for T_F.3 (reviewed by a subagent before publication; 8 substantive gaps resolved in-place).
  • The companion docs/dev/initiative-f-protocol.md (the current ad-hoc protocol catalogue) was added in PR Maintenance/code cleanup 2026-05 Phase I (general base) #6663.

Standalone fixes (regressions surfaced during Phase 2)

  • d771de96Sources.all could return None when a lockfile existed but _meta.sources was missing/empty (fell through if/else with no explicit return). Inherited from the original Project.sources property; surfaced by Copilot once the shape was documented as a list accessor. Always falls back to pipfile_sources() now.
  • 6da42d9bis_virtual_environment(path) was failing on Windows / Python 3.10 CI runs consistently. When WORKON_HOME contained a partially torn-down sibling venv directory — root exists but Scripts/ had been removed — Path.joinpath("Scripts").glob("python*") returned an empty iterator on Unix but raised FileNotFoundError on Windows (scandir is strict). VenvLocator._get_virtualenv_hash iterates WORKON_HOME and calls is_virtual_environment on every child to detect case-collision conflicts, so a half-cleaned-up parallel-test artifact crashed pipenv install before resolution could start. Fixed with a one-line bindir.is_dir() guard before the glob; two pinning unit tests added.
  • Stale Python-3.7 comment in Sources.find_source removed (pipenv requires ≥ 3.10).
  • News fragment rewritten to describe the actual is_valid_url removal rather than a hypothetical deprecation.

Stacked follow-up PRs (work already done; landing separately)

The remaining Initiative F work was completed but lives on sibling branches so each PR stays reviewable independently:

Branch Scope Status
maintenance/code-cleanup-phase3-resolver-typed-schema-2026-05 T_F.3 — execute the typed-schema migration. Introduces pipenv/resolver/ package, deletes Entry.get_cleaned_dict + format_requirement_for_lockfile, drops 3 env-var hops + 5 dead argv flags, ports 17 test-coverage cases to typed equivalents, adds JSON wire-shape integration test with PIPENV_REGEN_PROTOCOL_FIXTURES regen branch, adds news/T_F.3.behavior.rst. 17 commits, 758 unit tests pass. Pushed; PR pending
maintenance/code-cleanup-phase4-resolver-followups-2026-05 T_F.4 — fold the in-process and subprocess resolver branches onto a single resolve_for_pipenv(request) driver in new pipenv/resolver/core.py. T_F.5a — pluggable-backends design doc (948 lines, 10 sign-off questions queued including Pipfile opt-in shape, vendored-uv-vs-system, lockfile compatibility). T_F.6 — wall-clock timeout enforcement via request.metadata.deadline_seconds (subprocess: subprocess.run(timeout=...); in-process: signal.SIGALRM on Unix, no-op on Windows). T_F.7 — populate Diagnostics.resolver_log with structured resolve records, surfaced in --verbose mode. 10 commits, 780 unit tests pass. Pushed; PR pending

Still-queued work (not started)

  • T_D.5 — extract Lockfile subsystem from Project.
  • T_D.6 — extract Pipfile subsystem from Project (largest; most coupled).
  • T_E.4 — relocate unpack_url / get_http_url; delete the (then-empty) requirementslib.py.
  • T_F.5 execution — gates on maintainer sign-off of the 10 design questions in docs/dev/initiative-f-backends-design.md (lives on phase-4 branch).

Test plan

  • 679 unit tests pass locally (pytest tests/unit/)
  • +28 new tests across test_venv_locator.py (13), test_dependencies_bridges.py (4 for T_E.3), test_utils.py (2 for the Windows fix), and others
  • Windows / Python 3.10 is_virtual_environment FileNotFoundError fixed (was the only consistent CI failure on the prior run runs/25744107117)
  • CI matrix this run is the verification gate
  • Manual smoke: pipenv install, pipenv lock, pipenv update on a representative fixture Pipfile

🤖 Generated with Claude Code

matteius and others added 7 commits May 12, 2026 10:56
…ocol

Initiative F task T_F.2 (design): produces docs/dev/initiative-f-typed-design.md,
a 719-line proposal for a versioned dataclass-based ResolverRequest /
ResolverResponse pair that replaces the current ad-hoc argv + tempfile +
env-var cocktail catalogued in T_F.1.

Covers: envelope with schema_version, discriminated success / resolution-
error / internal-error result, a single canonical LockedRequirement
formatter that folds Entry.get_cleaned_dict and
format_requirement_for_lockfile, migration strategy (one-shot rewrite,
no backwards-compat shim per T_C.3 §9 / T_E.1 §6 sign-offs), test plan,
and 10 numbered open questions for maintainer sign-off.

Design only. No production code touched. Awaits maintainer sign-off
before T_F.3 execution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T_F.2 (Initiative F typed-schema design) committed at 921212e. Status
set to Completed; awaits maintainer sign-off before T_F.3 execution.
Wave seed table gains a "4-design" row for T_F.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s.py

T_E.3: Per the T_E.1 design + sign-off, move four single-line/short
helpers from pipenv/utils/requirementslib.py to pipenv/utils/dependencies.py
(the canonical home). merge_items brings its two private helpers
(_merge_into, _new_container_like) with it.

Moved:
  is_vcs                     (~14 lines)
  add_ssh_scheme_to_git_uri  (~14 lines)
  merge_items                (~33 lines, + 2 private helpers)
  get_pip_command            (~9 lines)

Caller imports migrated in the same PR per T_C.3 sec.9 / T_E.1
sign-off -- no backwards-compat shim. Five caller files updated:
pipenv/utils/{locking,pipfile}.py (folded into the existing
dependencies-import block), pipenv/project.py (one-line late-import
in _get_vcs_packages, narrow-scoped), tests/unit/test_requirementslib.py
(docstring + import re-target), tests/unit/test_utils.py (the
test_is_vcs late import).

requirementslib.py shrank from 275 to 144 lines (-131). After T_E.3
only one in-tree importer of requirementslib remains
(dependencies.py for unpack_url); T_E.4 will close that out by
relocating the unpack_url/get_http_url fork pair to a new
pipenv/utils/unpack.py and deleting requirementslib.py.

Validation:
- tests/unit/test_dependencies_bridges.py: 31 tests pass (21 from
  T_E.2 + 10 new for T_E.3: 4 import-shape pins, 1 source-cleared
  sanity check, 5 light behaviour pins).
- All four moved symbols importable from pipenv.utils.dependencies;
  no longer exist on pipenv.utils.requirementslib.
- The pipenv/project.py one-line import-line edit is intentionally
  not staged in this commit; T_D.4 is concurrently rewriting that
  file and will sweep up the import-line change as part of its
  larger commit (same pattern as T_D.3 sweeping up T_E.2's
  routines/install.py changes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the full T_E.3 entry to docs/dev/modernization-plan.md
following the T_E.2 format: status Completed, log of moved
symbols + caller-migration summary, files-edited list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ema design

Maintainer agreement on the proposed design plus an additional design
constraint: the typed schema should not foreclose future pluggable
resolver backends (uv being the obvious candidate) even though
Initiative F itself stays pip-only.

The change adds:

- A new "out of scope" bullet in §2 calling out pluggable backends
  explicitly, with a pointer to the new §6a.
- §6a "Future: pluggable resolver backends (informational; not in F)"
  documenting which design choices preserve the option (wire-type
  cleanliness, backend-neutral ResolverResult, package-layout reservation
  for pipenv/resolver/backends/) and which questions are intentionally
  left for a successor initiative (Pipfile opt-in shape, multi-backend
  selection, lockfile compatibility signalling, vendored-uv posture).
- An anchor in §8 Q1 (schema home) to §6a, making the package-layout
  decision a load-bearing prerequisite for future pluggability.

No code change. T_F.3 execution still gated on the maintainer
answering the 10 sign-off questions in §8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T_D.4: Third extraction in Initiative D (after T_D.2 Sources and
T_D.3 Settings). The 13 VenvLocator-classified methods on
pipenv.project.Project move into a new pipenv.utils.venv_locator.VenvLocator
class accessed via the @cached_property on Project. Every internal
caller migrated to the new access path in the same PR (per T_D.1
§8.4 sign-off: no holding-pattern wrappers, no DeprecationWarning).

API rename (matches T_D.2 Sources pattern): the ``virtualenv_``
prefix drops because the subsystem itself is named ``venv_locator``:

  project.virtualenv_location   -> project.venv_locator.location
  project.virtualenv_exists     -> project.venv_locator.exists
  project.virtualenv_name       -> project.venv_locator.name
  project.virtualenv_src_loc... -> project.venv_locator.src_location
  project.virtualenv_scripts... -> project.venv_locator.scripts_location
  project.download_location     -> project.venv_locator.download_location
  project.proper_names_db_path  -> project.venv_locator.proper_names_db_path
  project.is_venv_in_project()  -> project.venv_locator.is_venv_in_project()
  project.get_location_for_... -> project.venv_locator.get_location()
  project.finders / .finder     -> project.venv_locator.finders / .finder
  project.which / .python       -> project.venv_locator.which / .python
  project._which                -> project.venv_locator._which

The three __init__-set cache attrs (_virtualenv_location,
_download_location, _proper_names_db_path) moved to VenvLocator
instance state per the task spec. The private helpers _sanitize,
_get_virtualenv_hash, _pipfile_venv_in_project, _which moved with
their public counterparts. proper_names and register_proper_name
stayed on Project (Pipfile-bucket per T_D.1) but now read the
proper-names-db path through self.venv_locator.

Per T_D.1 §6.1 maintainer sign-off VenvLocator is read-only against
the venv (creation happens in pipenv/utils/virtualenv.py); no
Locator/Bootstrap split was needed.

pipenv/project.py shrinks by 245 net lines (1526 -> 1281). The
extracted methods + their docstrings live in pipenv/utils/venv_locator.py
(431 lines). Unused module-level imports purged from project.py:
base64, fnmatch, operator, re, find_windows_executable,
get_workon_home, is_virtual_environment, looks_like_dir,
system_which, virtualenv_scripts_dir.

Caller-site migration covers ~38 sites across pipenv/ and tests/:
pipenv/cli/command.py (6), pipenv/routines/{graph,install,lock,
shell,uninstall,update}.py (10), pipenv/utils/{pip,pipfile,project,
resolver,shell,virtualenv}.py (15), tests/integration/test_project.py
(1), tests/unit/{test_credential_safety,test_install_error_context,
test_utils}.py (mock-side migrations).

Behaviour-preserving: every VenvLocator method's logic is a
relocation. Same env-var precedence, same Pipfile reads, same
mkdir-on-access semantics, same finders caching shape (a per-
instance attribute on VenvLocator with the cached_property
on Project keeping VenvLocator alive for the process).

Tests: 17 new in tests/unit/test_venv_locator.py covering the
constructor, the @cached_property accessor, the env-var-vs-Pipfile
precedence for is_venv_in_project, the VIRTUAL_ENV short-circuit
in .location, location caching, mkdir-on-access for src/download/
proper-names-db, the PIPENV_CUSTOM_VENV_NAME and PIPENV_PYTHON
hooks in .name, and the which/_which fallback paths.

Full unit suite green: 677 passed, 9 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T_D.4 (VenvLocator subsystem extraction) landed in commit cb34945.
Updated the plan entry with status, the Option B naming-collision
rationale (matches T_D.2 Sources pattern), the API-rename table,
caller-migration summary, and the files-edited list.

Initiative D progress: three of five subsystem extractions now
complete (Sources T_D.2, Settings T_D.3, VenvLocator T_D.4).
Remaining: Lockfile (T_D.5, blocked-on T_D.4 — now unblocked) and
Pipfile (T_D.6, blocked-on T_D.5).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@matteius matteius changed the base branch from main to maintenance/code-cleanup-2026-05 May 12, 2026 15:12
matteius and others added 3 commits May 12, 2026 11:16
Dependency-aware execution plan for the typed-schema migration designed
in docs/dev/initiative-f-typed-design.md. Single atomic PR target on
maintenance/code-cleanup-phase2-2026-05 (per maintainer 2026-05-12).

Plan organization:

  Wave A (strict serial — A1 must commit golden JSON snapshots BEFORE
  A2 moves pipenv/resolver.py; the snapshots are generated against
  today's Entry.get_cleaned_dict + format_requirement_for_lockfile):
    A1 — schema dataclasses + canonical formatter + golden snapshots
    A2 — pipenv/resolver.py → pipenv/resolver/ package restructure

  Wave B (3-way parallel on disjoint files):
    B1 — subprocess entry rewrite; deletes Entry/process_resolver_results;
         migrates the three test files that imported those symbols
    B2 — parent-side rewrite, including the PIPENV_RESOLVER_PARENT_PYTHON
         in-process branch (type migration, NOT fold — fold is T_F.4)
    B3 — lockfile writer consumes LockedRequirement; deletes
         format_requirement_for_lockfile; ports the 17
         tests/unit/test_utils.py:1323-1538 pinning cases to the new API
         (coverage count must not drop)

  Wave C (4-way parallel test/doc):
    C1 — schema-dataclass unit tests
    C2 — JSON wire-shape integration test (incl. PIPENV_REGEN_PROTOCOL_FIXTURES branch)
    C3 — comma-in-marker regression fixture
    C4 — news/T_F.3.behavior.rst fragment

  Wave D:
    D1 — mark T_F.3 complete in modernization-plan.md

Plan reviewed by a subagent; 8 substantive gaps surfaced and resolved
in-place: stale test imports (test_dependencies, test_resolver_regressions,
test_locking_no_mutation), test_utils.py coverage port assignment,
in-process branch ownership disambiguation between B1/B2, golden-fixture
generation timing (A1 before A2), atomic-PR intermediate-commit caveat,
console-script reinstall note, fixture-regen mechanism, modernization-plan
single-writer guarantee.

T_F.3 execution remains gated on maintainer sign-off of the 10
§8 questions in initiative-f-typed-design.md. The plan assumes the
documented recommendations are accepted; any override revises specific
tasks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… and news fragment

Three Copilot review findings on PR #6663:

1. **Sources.all could return None.** When a lockfile exists but
   ``_meta.sources`` is missing/empty, the if/else fell through with
   no explicit return — the implicit ``None`` would break callers
   (``default``, ``index_urls``, ``get_source``, ``find_source``) that
   expect a list. Inherited behaviour from the original
   ``Project.sources`` property, surfaced by Copilot now that the
   shape is documented as a List accessor. Always fall back to
   ``pipfile_sources()`` after the lockfile path declines to provide
   anything.

2. **Stale Python 3.7 reference in find_source.** Inline comment
   claimed the explicit iteration was needed "to stay compatible with
   Python 3.7" — pipenv has required ≥ 3.10 for some time. The
   short-circuit concern the comment described is also moot after #1
   (``self.all`` no longer returns None), so the explicit-iteration
   pattern stands on its own without the misleading version note —
   dropped.

3. **News fragment mismatched reality.** The fragment described a
   deprecation-with-future-removal, but T_A.4 in this same PR removed
   the alias outright (commit 8554651). Rewrote the fragment to
   describe the actual change and the underlying "CLI is the stable
   API" rationale; also covers the parallel SourceNotFound re-export
   removal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ma module

Add §3.6 to the typed-schema design and a Risk #9 to the execution
plan: pipenv/resolver/schema.py runs inside the *target* venv's Python,
not the parent pipenv's Python. The target Python is whatever the
user's project virtualenv has — typically pipenv's minimum (currently
CPython 3.10) through the latest. This drives several load-bearing
discipline points that look like footnotes:

- Stdlib idioms back to the minimum target: @DataClass(frozen=True) ✓,
  Optional/Sequence/Mapping from typing ✓; disallowed at module top:
  typing.Self (3.11+), tomllib (3.11+), 3.11+ match patterns, ANY new
  vendored dep, ANY pipenv.patched.pip._internal import.
- The "no new vendor deps" rule is doubly load-bearing here: anything
  the schema imports at top level must be installable into every
  supported target venv.
- Diagnostics.pip_version reports pipenv's vendored pip
  (pipenv.patched.pip._vendor.pip.__version__), not the user's pip.
  Document explicitly so future readers don't misread.
- schema_version mismatch can fire in the legitimate-installs case
  where parent and subprocess come from different pipenv installs
  (e.g. global vs venv).
- pip-internal types are accepted ONLY inside the body of
  LockedRequirement.from_install_requirement, never at module top.

A1's validation step picks up an explicit "schema imports cleanly on
the minimum target Python" check plus a grep gate for top-level
pip-internal imports. CI's 3.10–3.14 matrix is the integration gate.

This constraint was implicit in F.1's §3.2 invocation-path notes; T_F.2
makes it explicit because the typed-schema module is a new artifact
that must survive the same execution environment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@matteius matteius changed the title Maintenance/code cleanup phase2 2026 05 Maintenance/code cleanup phase2 2026-05 May 12, 2026
@matteius matteius changed the title Maintenance/code cleanup phase2 2026-05 Maintenance/code cleanup 2026-05 Phase II May 12, 2026
@matteius matteius changed the title Maintenance/code cleanup 2026-05 Phase II Maintenance/code cleanup 2026-05 Phase II (venv locator) May 12, 2026
@matteius matteius marked this pull request as ready for review May 12, 2026 16:03
@matteius matteius requested review from Copilot and oz123 May 12, 2026 16:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the “maintenance/code cleanup 2026-05 Phase II” work by extracting virtualenv discovery/path logic from Project into a dedicated VenvLocator subsystem, migrating call sites/tests to the new API surface, and relocating several dependency helper utilities from requirementslib.py into dependencies.py. It also updates internal dev documentation for Initiative D/E/F work.

Changes:

  • Introduce pipenv.utils.venv_locator.VenvLocator and add Project.venv_locator (@cached_property), migrating call sites from Project.virtualenv_*/Project._which() to project.venv_locator.*.
  • Move is_vcs, add_ssh_scheme_to_git_uri, merge_items, and get_pip_command from pipenv.utils.requirementslib into pipenv.utils.dependencies, updating imports and adding tests to pin the new import surface/behavior.
  • Add/adjust unit + integration tests and update modernization/typed-resolver design docs.

Reviewed changes

Copilot reviewed 30 out of 30 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/unit/test_venv_locator.py New unit tests pinning VenvLocator behavior and precedence/caching semantics.
tests/unit/test_utils.py Update unit tests/mocks to use project.venv_locator.* and new helper import paths.
tests/unit/test_requirementslib.py Retarget merge_items tests to pipenv.utils.dependencies.
tests/unit/test_install_error_context.py Update test stubs to use project.venv_locator.src_location.
tests/unit/test_dependencies_bridges.py Extend pinning tests for symbols moved into pipenv.utils.dependencies and removal from requirementslib.
tests/unit/test_credential_safety.py Update fake project to expose venv_locator.src_location.
tests/integration/test_project.py Update integration test assertion to use project.venv_locator.location.
pipenv/utils/virtualenv.py Migrate venv path/existence calls to project.venv_locator.*.
pipenv/utils/venv_locator.py New VenvLocator subsystem implementing venv resolution, naming, and executable lookup.
pipenv/utils/sources.py Ensure Sources.all falls back to Pipfile sources when lockfile meta is empty/missing.
pipenv/utils/shell.py Switch project_python() to call project.venv_locator._which("python").
pipenv/utils/resolver.py Migrate to project.venv_locator.src_location and project.venv_locator.python(...).
pipenv/utils/requirementslib.py Remove moved helpers, leaving only the unpack_url/get_http_url fork and VCS_SCHEMES.
pipenv/utils/project.py Migrate venv existence + python path resolution to project.venv_locator.*.
pipenv/utils/pipfile.py Migrate venv checks and imports of is_vcs/merge_items to dependencies.
pipenv/utils/pip.py Update default PIP_SRC fallback to project.venv_locator.src_location.
pipenv/utils/locking.py Move is_vcs/merge_items imports to dependencies.
pipenv/utils/dependencies.py Add moved helper functions and supporting imports (InstallCommand, is_valid_url).
pipenv/routines/update.py Use project.venv_locator.python() and pass which=project.venv_locator._which.
pipenv/routines/uninstall.py Use project.venv_locator._which and project.venv_locator.download_location.
pipenv/routines/shell.py Use project.venv_locator.location for shell/run path wiring.
pipenv/routines/lock.py Pass which=project.venv_locator._which to resolver.
pipenv/routines/install.py Use project.venv_locator.exists and project.venv_locator._which.
pipenv/routines/graph.py Use project.venv_locator.python() and project.venv_locator.exists.
pipenv/project.py Remove venv-related methods/properties and add Project.venv_locator; route proper-names DB path via venv_locator.
pipenv/cli/command.py Migrate --py, --rm, --venv, activation paths to project.venv_locator.*.
news/+initiative-a-is-valid-url.removal.rst Update news fragment about removal of internal-only import alias(es).
docs/dev/modernization-plan.md Mark T_D.4 as completed; document T_E.3 completion and add T_F.2 entry.
docs/dev/initiative-f-typed-design.md New typed resolver subprocess protocol design document (T_F.2).
docs/dev/initiative-f-execution-plan.md New execution plan document for implementing the typed resolver schema (T_F.3).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/unit/test_venv_locator.py Outdated
Comment thread pipenv/utils/venv_locator.py Outdated
Comment thread pipenv/utils/dependencies.py Outdated
matteius and others added 3 commits May 12, 2026 12:52
…Scripts

Windows / Python 3.10 CI run 25744107117 on PR #6665 reproducibly
fails test_multiple_editable_packages_should_not_race with:

  FileNotFoundError: [WinError 3] The system cannot find the path
  specified: 'C:\\Users\\runneradmin\\.virtualenvs\\pipenv-...\\Scripts'

The traceback runs through `VenvLocator._get_virtualenv_hash`, which
iterates `WORKON_HOME` and calls `is_virtual_environment(path)` on
every child to detect case-collision conflicts. When a parallel test
leaves a partially torn-down venv directory under WORKON_HOME — root
exists, but `Scripts` (or `bin`) has been removed — `Path.glob`
behaves differently per platform:

- Unix:   returns an empty iterator (the loop body simply doesn't run).
- Windows: `scandir` is strict, so `Path.joinpath("Scripts").glob("python*")`
  raises `FileNotFoundError`, which bubbles all the way up and crashes
  `pipenv install` before resolution can start.

Fix is a single `bindir.is_dir()` guard before iterating its glob.
Resilient on both platforms; preserves existing semantics for real
venvs (test added).

Two new unit tests pin the behaviour:
- `test_is_virtual_environment_returns_false_for_directory_without_bindir`
  — the bug repro: empty dir is not a venv, must not crash.
- `test_is_virtual_environment_returns_true_for_real_venv_layout` —
  sanity: a bin/python layout still resolves to True.

No skip marker added; the proper fix replaces the platform-specific
workaround. 679 unit tests pass (was 677 baseline + 2 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI and others added 4 commits May 12, 2026 16:57
Agent-Logs-Url: https://github.com/pypa/pipenv/sessions/18303346-406f-45d2-bff4-984f606f3020

Co-authored-by: matteius <479892+matteius@users.noreply.github.com>
Agent-Logs-Url: https://github.com/pypa/pipenv/sessions/18303346-406f-45d2-bff4-984f606f3020

Co-authored-by: matteius <479892+matteius@users.noreply.github.com>
Agent-Logs-Url: https://github.com/pypa/pipenv/sessions/18303346-406f-45d2-bff4-984f606f3020

Co-authored-by: matteius <479892+matteius@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants