Skip to content

Python: accept a caller-provisioned dependency path for ty type attribution#7930

Merged
knutwannheden merged 1 commit into
mainfrom
fix-python-self-receiver-supertype-attribution
Jun 7, 2026
Merged

Python: accept a caller-provisioned dependency path for ty type attribution#7930
knutwannheden merged 1 commit into
mainfrom
fix-python-self-receiver-supertype-attribution

Conversation

@knutwannheden

@knutwannheden knutwannheden commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Motivation

ty resolves a first-party class's third-party supertypes — e.g. class User(BaseModel) where BaseModel comes from pydantic — only when the project's dependencies are discoverable. During a parse the interpreter running the RPC server has just openrewrite + ty-types installed, not the project's deps, so ty emits the base as dynamic, the supertype maps to JavaType.Unknown, the class's _supertype stays None, and receiver-type checks such as is_assignable_to("pydantic.main.BaseModel", self.type) never fire.

Rather than have the parser provision dependencies itself, this gives it a way to be pointed at an already-provisioned environment. The parser becomes a pure consumer; provisioning is the caller's responsibility — whatever orchestrates the parse and has already resolved the project's dependencies, or a test/template helper. This mirrors how the JavaScript parser resolves against an already-installed node_modules.

This PR is the parser-side mechanism only. No caller forwards a path yet, so behavior is unchanged on main; forwarding a per-project dependency environment from the parse orchestration is follow-up work.

Example

// The caller provisions a venv with the project's dependencies, then forwards it:
Path depsVenv = /* provisioned by the caller */;
rpc.parseProject(
        projectDir,
        ParseProjectOptions.builder()
                .relativeTo(repoRoot)
                .dependencyPath(depsVenv)
                .build(),
        ctx);

The new optional inputs are bundled into a @Builder value object instead of growing parseProject another positional parameter (two adjacent nullable Paths — relativeTo, dependencyPath — are easy to transpose), following the spirit of the recent GitProvenance.CommitHistory addition.

Summary

  • Add ParseProjectOptions (exclusions, relativeTo, dependencyPath) and parseProject(Path, ParseProjectOptions, ExecutionContext) — the only overload that accepts a dependencyPath.
  • Deprecate the positional overloads that pass optional config (exclusions / relativeTo); they delegate to the new method. The no-config parseProject(Path, ExecutionContext) convenience is kept.
  • ParseProject RPC request carries dependencyPath.
  • server.py handle_parse / handle_parse_project read the forwarded dependencyPath and pass it to TyTypesClient(virtual_env=...). The in-handler auto-provisioning helper (_resolve_dependency_venv) is removed — the handler no longer builds environments. With it gone, the dependency-workspace builder is now used only by templates and tests (a test builds the venv it forwards).

Test plan

  • New TestDependencyPathForwarding (fast, deterministic): both handlers forward the path to TyTypesClient; with no dependencyPath no auto-provisioning occurs.
  • Reworked TestExternalSupertypeResolutionInParsePath (real ty + uv): a forwarded venv resolves the self receiver as a pydantic.main.BaseModel subclass; with no path the supertype does not resolve.
  • tests/python: 1532 passed, 6 skipped.
  • :rewrite-python:compileJava + :rewrite-python:compileTestJava clean (no internal deprecated-usage warnings).
  • :rewrite-python:integTest --tests ParseProjectIntegTest passes (refactored parseProject path is behavior-preserving).
  • One RPC bridge test (AssignTest) passes (serialization of the new ParseProject shape is intact).

@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Jun 7, 2026
@knutwannheden knutwannheden force-pushed the fix-python-self-receiver-supertype-attribution branch from 305db88 to cdfb527 Compare June 7, 2026 08:23
@knutwannheden knutwannheden changed the title rewrite-python: accept a caller-provisioned dependency path for ty type attribution Python: accept a caller-provisioned dependency path for ty type attribution Jun 7, 2026
@knutwannheden knutwannheden changed the title Python: accept a caller-provisioned dependency path for ty type attribution Python: accept a caller-provisioned dependency path for ty type attribution Jun 7, 2026
…pe attribution

ty resolves a first-party class's third-party supertypes (e.g. `class
User(BaseModel)` where `BaseModel` is pydantic) only when the project's
dependencies are discoverable. The interpreter running the parse has only
openrewrite + ty-types installed, so without the project's deps ty emits the
base as `dynamic`, the supertype maps to `JavaType.Unknown`, and receiver-type
checks like `is_assignable_to("pydantic.main.BaseModel", ...)` fail.

Give the parser a way to be pointed at the project's dependencies instead of
provisioning them itself: parseProject now accepts a `dependencyPath` (a venv
with the deps installed). The handler forwards it to ty-types via VIRTUAL_ENV
and never builds an environment on its own — provisioning is the caller's
responsibility (whatever orchestrates the parse and has already resolved the
project's dependencies, or a test/template helper), mirroring how the
JavaScript parser resolves against an already-installed node_modules.

The new input is carried by a Lombok-builder value object, ParseProjectOptions,
rather than another positional parameter, so the API doesn't accrue a tail of
adjacent same-typed arguments.

- Add ParseProjectOptions (exclusions, relativeTo, dependencyPath) and
  parseProject(Path, ParseProjectOptions, ExecutionContext). The positional
  overloads that pass optional config (exclusions / relativeTo) are deprecated
  in favor of it and delegate to it; the no-config parseProject(Path, ctx)
  convenience is kept. ParseProject RPC request carries dependencyPath.
- server.py handle_parse / handle_parse_project read the forwarded dependencyPath
  and pass it to TyTypesClient(virtual_env=...); the in-handler auto-provisioning
  helper is removed (the handler is now a pure consumer). With it gone, the
  dependency workspace builder is now used only by templates and tests.
@knutwannheden knutwannheden force-pushed the fix-python-self-receiver-supertype-attribution branch from cdfb527 to aa6a497 Compare June 7, 2026 08:25
@knutwannheden knutwannheden marked this pull request as ready for review June 7, 2026 08:28
@knutwannheden knutwannheden merged commit 72edd51 into main Jun 7, 2026
1 check passed
@knutwannheden knutwannheden deleted the fix-python-self-receiver-supertype-attribution branch June 7, 2026 08:45
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant