Python: accept a caller-provisioned dependency path for ty type attribution#7930
Merged
knutwannheden merged 1 commit intoJun 7, 2026
Merged
Conversation
305db88 to
cdfb527
Compare
ty type attribution
…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.
cdfb527 to
aa6a497
Compare
This was referenced Jun 7, 2026
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.
Motivation
ty resolves a first-party class's third-party supertypes — e.g.
class User(BaseModel)whereBaseModelcomes from pydantic — only when the project's dependencies are discoverable. During a parse the interpreter running the RPC server has justopenrewrite+ty-typesinstalled, not the project's deps, so ty emits the base asdynamic, the supertype maps toJavaType.Unknown, the class's_supertypestaysNone, and receiver-type checks such asis_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 new optional inputs are bundled into a
@Buildervalue object instead of growingparseProjectanother positional parameter (two adjacent nullablePaths —relativeTo,dependencyPath— are easy to transpose), following the spirit of the recentGitProvenance.CommitHistoryaddition.Summary
ParseProjectOptions(exclusions,relativeTo,dependencyPath) andparseProject(Path, ParseProjectOptions, ExecutionContext)— the only overload that accepts adependencyPath.exclusions/relativeTo); they delegate to the new method. The no-configparseProject(Path, ExecutionContext)convenience is kept.ParseProjectRPC request carriesdependencyPath.server.pyhandle_parse/handle_parse_projectread the forwardeddependencyPathand pass it toTyTypesClient(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
TestDependencyPathForwarding(fast, deterministic): both handlers forward the path toTyTypesClient; with nodependencyPathno auto-provisioning occurs.TestExternalSupertypeResolutionInParsePath(real ty + uv): a forwarded venv resolves theselfreceiver as apydantic.main.BaseModelsubclass; with no path the supertype does not resolve.tests/python: 1532 passed, 6 skipped.:rewrite-python:compileJava+:rewrite-python:compileTestJavaclean (no internal deprecated-usage warnings).:rewrite-python:integTest --tests ParseProjectIntegTestpasses (refactoredparseProjectpath is behavior-preserving).AssignTest) passes (serialization of the newParseProjectshape is intact).