This commit is contained in:
Wizzerinus | Alex K. 2025-12-23 09:56:56 +01:00 committed by GitHub
commit 578424d8ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 89 additions and 21 deletions

View file

@ -76,3 +76,63 @@ def reveal_type(obj, /): ...
```py
reveal_type(foo) # revealed: Unknown
```
## Builtins imported from custom project-level stubs
The project can add or replace builtins with the `__builtins__.pyi` stub. They will take precedence
over the typeshed ones.
```py
reveal_type(foo) # revealed: int
reveal_type(bar) # revealed: str
reveal_type(quux(1)) # revealed: int
b = baz # error: [unresolved-reference]
reveal_type(ord(100)) # revealed: bool
a = ord("a") # error: [invalid-argument-type]
bar = int(123)
reveal_type(bar) # revealed: int
```
`__builtins__.pyi`:
```pyi
foo: int = ...
bar: str = ...
def quux(value: int) -> int: ...
unused: str = ...
def ord(x: int) -> bool: ...
```
Builtins stubs are searched relative to the project root, not the file using them.
`under/some/folder.py`:
```py
reveal_type(foo) # revealed: int
reveal_type(bar) # revealed: str
```
## Assigning custom builtins
```py
import builtins
builtins.foo = 123
builtins.bar = 456 # error: [unresolved-attribute]
builtins.baz = 789 # error: [invalid-assignment]
builtins.chr = lambda x: str(x) # error: [invalid-assignment]
builtins.chr = 10
```
`__builtins__.pyi`:
```pyi
foo: int
baz: str
chr: int
```

View file

@ -15,7 +15,7 @@ use crate::types::{
Truthiness, Type, TypeAndQualifiers, TypeQualifiers, UnionBuilder, UnionType, binding_type,
declaration_type, todo_type,
};
use crate::{Db, FxOrderSet, Program};
use crate::{Db, FxOrderSet, Module, ModuleName, Program};
pub(crate) use implicit_globals::{
module_type_implicit_global_declaration, module_type_implicit_global_symbol,
@ -380,25 +380,29 @@ pub(crate) fn imported_symbol<'db>(
/// and should not be used when a symbol is being explicitly imported from the `builtins` module
/// (e.g. `from builtins import int`).
pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> PlaceAndQualifiers<'db> {
resolve_module_confident(db, &KnownModule::Builtins.name())
.and_then(|module| {
let file = module.file(db)?;
Some(
symbol_impl(
db,
global_scope(db, file),
symbol,
RequiresExplicitReExport::Yes,
ConsideredDefinitions::EndOfScope,
)
.or_fall_back_to(db, || {
// We're looking up in the builtins namespace and not the module, so we should
// do the normal lookup in `types.ModuleType` and not the special one as in
// `imported_symbol`.
module_type_implicit_global_symbol(db, symbol)
}),
)
})
let resolver = |module: Module<'_>| {
let file = module.file(db)?;
let found_symbol = symbol_impl(
db,
global_scope(db, file),
symbol,
RequiresExplicitReExport::Yes,
ConsideredDefinitions::EndOfScope,
)
.or_fall_back_to(db, || {
// We're looking up in the builtins namespace and not the module, so we should
// do the normal lookup in `types.ModuleType` and not the special one as in
// `imported_symbol`.
module_type_implicit_global_symbol(db, symbol)
});
// If this symbol is not present in project-level builtins, search in the default ones.
found_symbol
.ignore_possibly_undefined()
.map(|_| found_symbol)
};
resolve_module_confident(db, &ModuleName::new("__builtins__").unwrap())
.and_then(&resolver)
.or_else(|| resolve_module_confident(db, &KnownModule::Builtins.name()).and_then(resolver))
.unwrap_or_default()
}

View file

@ -4963,7 +4963,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
Type::ModuleLiteral(module) => {
if let Place::Defined(attr_ty, _, _) = module.static_member(db, attribute).place {
let sym = match module.module(db).name(db).as_str() {
"builtins" => builtins_symbol(db, attribute),
_ => module.static_member(db, attribute),
};
if let Place::Defined(attr_ty, _, _) = sym.place {
let value_ty = infer_value_ty(self, TypeContext::new(Some(attr_ty)));
let assignable = value_ty.is_assignable_to(db, attr_ty);