-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[ty] Implement stdlib stub mapping #19529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
let real_stdlib_path = python_environment.as_ref().and_then(|python_environment| { | ||
// For now this is considered non-fatal, we don't Need this for anything. | ||
python_environment.real_stdlib_path(system).ok() | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As implemented this will redo a lot of work the site-packages path does -- for now I'm leaving them separate in case the implementations should diverge more, but I can merge them together once we're more confident.
// Append the real stdlib as the very last option in search. | ||
// Normally this means it will always be shadowed by typeshed. | ||
// | ||
// FIXME(Gankra): ideally this should be completely disabled unless we're in | ||
// `ModuleResolveMode::NoStubsAllowed`. | ||
if let Some(real_stdlib_path) = real_stdlib_path { | ||
dynamic_paths.push(real_stdlib_path.clone()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to iterate on this part.
// FIXME(Gankra): should we inherit info from the parent environment? | ||
parent_environment: _, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should be using this? But I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think base_executable_home_path
should always be the same for the parent environment? It would be pretty weird if not? But you're the uv expert here ;)
crates/ty_test/src/lib.rs
Outdated
// FIXME(Gankra): should this be computed properly? | ||
real_stdlib_path: None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Note: this is in testing code)
// Append the real stdlib as the very last option in search. | ||
// Normally this means it will always be shadowed by typeshed. | ||
// | ||
// FIXME(Gankra): ideally this should be completely disabled unless we're in | ||
// `ModuleResolveMode::NoStubsAllowed`. | ||
if let Some(real_stdlib_path) = real_stdlib_path { | ||
dynamic_paths.push(real_stdlib_path.clone()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, I'm not sure it's correct for this to come after e.g. the site-packages
search path. I think this would mean that if e.g. the user had the moribund asyncio
package from PyPI installed into their venv, they had import asyncio
in a file, and they did go-to-definition on the asyncio
symbol, we'd jump to the module in the venv's site-packages directory. But at runtime, the module in the venv's site-packages directory will be totally ignored: the stdlib takes precedence in module resolution over third-party packages, so import asyncio
will resolve to the stdlib asyncio
module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh sorry, I posted this comment before I saw your comment above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No this is still a great insight, thanks!
Here's the things I'd want to manually double-check that this works with:
(If it doesn't work with all of them right now, obviously fine to TODO some of them and leave them for followups!) |
Hmm ok so this works great with uv-managed cpython/pypy/graalpy but need to work harder for homebrew at least. It looks like they go out of their way to make site-packages follow this format, but the stdlib is somewhere else. Notably the python binary it refers to is symlinked, and if we follow that symlink then this logic once again works (the stdlib is in fact in |
by using essentially the same logic for system site-packages, on the assumption that system site-packages are always a subdir of the stdlib we were looking for.
Newest commit handles homebrew and the macos system python. |
Diagnostic diff on typing conformance testsNo changes detected when running ty on typing conformance tests ✅ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
Would it be very involved to write a test for this similar to the other tests we have in site_packages
?
let stdlib_path = if stdlib_path_is_shadowed { | ||
None | ||
} else { | ||
Some(stdlib_path) | ||
}; | ||
let real_stdlib_path = if real_stdlib_path_is_shadowed { | ||
None | ||
} else { | ||
real_stdlib_path | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious to hear @AlexWaygood's thoughts on this. I suspect that ty's results will be fairly bonkers when the standard library is shadowed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They mentioned astral-sh/ty#523
if matches!(implementation, PythonImplementation::CPython) | ||
&& python_version.is_some_and(PythonVersion::free_threaded_build_available) | ||
{ | ||
let alternative_path = | ||
sys_prefix_path.join(format!("lib/python{}t", python_version.unwrap())); | ||
if system.is_directory(&alternative_path) { | ||
return Ok(alternative_path); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: If a small preference to use a match
or let
here for python_version
to avoid the unwrap. It's, unfortunately, a bit more verbose without if let chains (only one more month before we can use them)
if matches!(implementation, PythonImplementation::CPython) | |
&& python_version.is_some_and(PythonVersion::free_threaded_build_available) | |
{ | |
let alternative_path = | |
sys_prefix_path.join(format!("lib/python{}t", python_version.unwrap())); | |
if system.is_directory(&alternative_path) { | |
return Ok(alternative_path); | |
} | |
} | |
} | |
match (implementation, python_version) { | |
(PythonImplementation::CPython, Some(python_version)) if python_version.free_threaded_build_available() => { | |
let alternative_path = | |
sys_prefix_path.join(format!("lib/python{}t", python_version.unwrap())); | |
if system.is_directory(&alternative_path) { | |
return Ok(alternative_path); | |
} | |
} | |
} | |
if matches!(implementation, PythonImplementation::CPython) | |
&& python_version.is_some_and(PythonVersion::free_threaded_build_available) | |
{ | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that this is copy-pasted logic from the equivalent site-packages logic (prioritized keeping them the same where i can)
I am going to land this, I'm reasonably confident in how safe the implementation is. I expect more bugs will come up with us failing to find Fun stdlibs but this is still a big win. |
* main: (31 commits) Add AIR301 rule (#17707) Avoid underflow in default ranges before a BOM (#19839) Update actions/download-artifact digest to de96f46 (#19852) Update docker/login-action action to v3.5.0 (#19860) Update rui314/setup-mold digest to 7344740 (#19853) Update cargo-bins/cargo-binstall action to v1.14.4 (#19855) Update actions/cache action to v4.2.4 (#19854) Update Rust crate hashbrown to v0.15.5 (#19858) Update Rust crate camino to v1.1.11 (#19857) Update Rust crate proc-macro2 to v1.0.96 (#19859) Update dependency ruff to v0.12.8 (#19856) SIM905: Fix handling of U+001C..U+001F whitespace (#19849) RUF064: offer a safe fix for multi-digit zeros (#19847) Clean up unused rendering code in `ruff_linter` (#19832) [ty] Add Salsa caching to `TupleType::to_class_type` (#19840) [ty] Handle cycles when finding implicit attributes (#19833) [ty] fix goto-definition on imports (#19834) [ty] Implement stdlib stub mapping (#19529) [`flake8-comprehensions`] Fix false positive for `C420` with attribute, subscript, or slice assignment targets (#19513) [ty] Implement module-level `__getattr__` support (#19791) ...
by using essentially the same logic for system site-packages, on the assumption that system site-packages are always a subdir of the stdlib we were looking for.
This requires some more testing, but at least on my macbook it lets me jump to e.g.
warnings.deprecated
.