Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ is unbound.

```py
reveal_type(__name__) # revealed: str
reveal_type(__file__) # revealed: str | None
# Typeshed says this is str | None, but for a pure-Python on-disk module its always str
reveal_type(__file__) # revealed: str
reveal_type(__loader__) # revealed: LoaderProtocol | None
reveal_type(__package__) # revealed: str | None
reveal_type(__doc__) # revealed: str | None
Expand Down Expand Up @@ -52,6 +53,10 @@ import typing
reveal_type(typing.__name__) # revealed: str
reveal_type(typing.__init__) # revealed: bound method ModuleType.__init__(name: str, doc: str | None = ellipsis) -> None

# For a stub module, we don't know that `__file__` is a string (at runtime it may be entirely
# unset, but we follow typeshed here):
reveal_type(typing.__file__) # revealed: str | None

# These come from `builtins.object`, not `types.ModuleType`:
reveal_type(typing.__eq__) # revealed: bound method ModuleType.__eq__(value: object, /) -> bool

Expand Down
8 changes: 7 additions & 1 deletion crates/ty_python_semantic/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -982,12 +982,18 @@ mod implicit_globals {
db: &'db dyn Db,
name: &str,
) -> SymbolAndQualifiers<'db> {
// We special-case `__file__` here because we know that for an internal implicit global
// lookup in a Python module, it is always a string, even though typeshed says `str |
// None`.
if name == "__file__" {
Symbol::bound(KnownClass::Str.to_instance(db)).into()
}
// In general we wouldn't check to see whether a symbol exists on a class before doing the
// `.member()` call on the instance type -- we'd just do the `.member`() call on the instance
// type, since it has the same end result. The reason to only call `.member()` on `ModuleType`
// when absolutely necessary is that this function is used in a very hot path (name resolution
// in `infer.rs`). We use less idiomatic (and much more verbose) code here as a micro-optimisation.
if module_type_symbols(db)
else if module_type_symbols(db)
.iter()
.any(|module_type_member| &**module_type_member == name)
{
Expand Down
Loading