[ty] don't allow first-party code to shadow stdlib types module (#19128)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

This commit is contained in:
Carl Meyer 2025-07-04 03:36:26 -07:00 committed by GitHub
parent 25bdb67d9a
commit 7712c2fd15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 10 deletions

View file

@ -205,3 +205,18 @@ python-version = "3.13"
import aifc # error: [unresolved-import] import aifc # error: [unresolved-import]
from distutils import sysconfig # error: [unresolved-import] from distutils import sysconfig # error: [unresolved-import]
``` ```
## Cannot shadow core standard library modules
`types.py`:
```py
x: int
```
```py
# error: [unresolved-import]
from types import x
from types import FunctionType
```

View file

@ -535,14 +535,23 @@ struct ModuleNameIngredient<'db> {
pub(super) name: ModuleName, pub(super) name: ModuleName,
} }
/// Returns `true` if the module name refers to a standard library module which can't be shadowed
/// by a first-party module.
///
/// This includes "builtin" modules, which can never be shadowed at runtime either, as well as the
/// `types` module, which tends to be imported early in Python startup, so can't be consistently
/// shadowed, and is important to type checking.
fn is_non_shadowable(minor_version: u8, module_name: &str) -> bool {
module_name == "types" || ruff_python_stdlib::sys::is_builtin_module(minor_version, module_name)
}
/// Given a module name and a list of search paths in which to lookup modules, /// Given a module name and a list of search paths in which to lookup modules,
/// attempt to resolve the module name /// attempt to resolve the module name
fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<ResolvedName> { fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<ResolvedName> {
let program = Program::get(db); let program = Program::get(db);
let python_version = program.python_version(db); let python_version = program.python_version(db);
let resolver_state = ResolverContext::new(db, python_version); let resolver_state = ResolverContext::new(db, python_version);
let is_builtin_module = let is_non_shadowable = is_non_shadowable(python_version.minor, name.as_str());
ruff_python_stdlib::sys::is_builtin_module(python_version.minor, name.as_str());
let name = RelaxedModuleName::new(name); let name = RelaxedModuleName::new(name);
let stub_name = name.to_stub_package(); let stub_name = name.to_stub_package();
@ -553,7 +562,8 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<ResolvedName> {
// the module name always resolves to the stdlib module, // the module name always resolves to the stdlib module,
// even if there's a module of the same name in the first-party root // even if there's a module of the same name in the first-party root
// (which would normally result in the stdlib module being overridden). // (which would normally result in the stdlib module being overridden).
if is_builtin_module && !search_path.is_standard_library() { // TODO: offer a diagnostic if there is a first-party module of the same name
if is_non_shadowable && !search_path.is_standard_library() {
continue; continue;
} }

View file

@ -116,13 +116,6 @@ fn run_corpus_tests(pattern: &str) -> anyhow::Result<()> {
.with_context(|| format!("Failed to read test file: {path}"))?; .with_context(|| format!("Failed to read test file: {path}"))?;
let mut check_with_file_name = |path: &SystemPath| { let mut check_with_file_name = |path: &SystemPath| {
if relative_path.file_name() == Some("types.pyi") {
println!(
"Skipping {relative_path:?}: paths with `types.pyi` as their final segment cause a stack overflow"
);
return;
}
db.memory_file_system().write_file_all(path, &code).unwrap(); db.memory_file_system().write_file_all(path, &code).unwrap();
File::sync_path(&mut db, path); File::sync_path(&mut db, path);