Skip to content
Closed
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 @@ -237,11 +237,11 @@ b: SomeUnknownName = 1 # error: [unresolved-reference]
```py
from mod import a, b

reveal_type(a) # revealed: Unknown | Literal[1]
reveal_type(a) # revealed: int
reveal_type(b) # revealed: Unknown

# All external modifications of `a` are allowed:
a = None
a = None # error: [invalid-assignment]
```

### Undeclared and possibly unbound
Expand All @@ -265,11 +265,11 @@ if flag:
# on top of this document.
from mod import a, b

reveal_type(a) # revealed: Unknown | Literal[1]
reveal_type(a) # revealed: int
reveal_type(b) # revealed: Unknown

# All external modifications of `a` are allowed:
a = None
a = None # error: [invalid-assignment]
```

### Undeclared and unbound
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_python_semantic/resources/mdtest/del.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def foo():
global x
def bar():
# allowed, refers to `x` in the global scope
reveal_type(x) # revealed: Unknown | Literal[1]
reveal_type(x) # revealed: int
bar()
del x # allowed, deletes `x` in the global scope (though we don't track that)
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ reveal_type(y)
# error: [possibly-missing-import] "Member `y` of module `maybe_unbound` may be missing"
from maybe_unbound import x, y

reveal_type(x) # revealed: Unknown | Literal[3]
reveal_type(y) # revealed: Unknown | Literal[3]
reveal_type(x) # revealed: int
reveal_type(y) # revealed: int
```

## Maybe unbound annotated
Expand Down Expand Up @@ -56,7 +56,7 @@ Importing an annotated name prefers the declared type over the inferred type:
# error: [possibly-missing-import] "Member `y` of module `maybe_unbound_annotated` may be missing"
from maybe_unbound_annotated import x, y

reveal_type(x) # revealed: Unknown | Literal[3]
reveal_type(x) # revealed: int
reveal_type(y) # revealed: int
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ class A: ...
from subexporter import *

# TODO: Should we avoid including `Unknown` for this case?
reveal_type(__all__) # revealed: Unknown | list[Unknown | str]
reveal_type(__all__) # revealed: list[Unknown | str]

__all__.append("B")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ def __getattr__(name: str) -> int:
import mixed_module

# Explicit attribute should take precedence
reveal_type(mixed_module.explicit_attr) # revealed: Unknown | Literal["explicit"]
reveal_type(mixed_module.explicit_attr) # revealed: str

# `__getattr__` should handle unknown attributes
reveal_type(mixed_module.dynamic_attr) # revealed: str
reveal_type(mixed_module.dynamic_attr) # revealed: int
```

`mixed_module.py`:

```py
explicit_attr = "explicit"

def __getattr__(name: str) -> str:
return "dynamic"
def __getattr__(name: str) -> int:
return 1
```

## Precedence: submodules vs `__getattr__`
Expand Down
10 changes: 7 additions & 3 deletions crates/ty_python_semantic/resources/mdtest/import/namespace.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,23 @@ If there's a namespace package with the same name as a module, the module takes
`foo.py`:

```py
x = "module"
class FromModule: ...

x = FromModule
```

`foo/bar.py`:

```py
x = "namespace"
class FromNamespace: ...

x = FromNamespace
```

```py
from foo import x

reveal_type(x) # revealed: Unknown | Literal["module"]
reveal_type(x) # revealed: <class 'FromModule'>

import foo.bar # error: [unresolved-import]
```
Expand Down
10 changes: 5 additions & 5 deletions crates/ty_python_semantic/resources/mdtest/import/star.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ X = (Y := 3) + 4
```py
from exporter import *

reveal_type(X) # revealed: Unknown | Literal[7]
reveal_type(Y) # revealed: Unknown | Literal[3]
reveal_type(X) # revealed: int
reveal_type(Y) # revealed: int
```

### Global-scope symbols defined in many other ways
Expand Down Expand Up @@ -781,9 +781,9 @@ else:
from exporter import *

# error: [possibly-unresolved-reference]
reveal_type(A) # revealed: Unknown | Literal[1]
reveal_type(A) # revealed: int

reveal_type(B) # revealed: Unknown | Literal[2, 3]
reveal_type(B) # revealed: int
```

### Reachability constraints in the importing module
Expand All @@ -804,7 +804,7 @@ if coinflip():
from exporter import *

# error: [possibly-unresolved-reference]
reveal_type(A) # revealed: Unknown | Literal[1]
reveal_type(A) # revealed: int
```

### Reachability constraints in the exporting module *and* the importing module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ TYPE_CHECKING: bool = ...
```py
from constants import TYPE_CHECKING

reveal_type(TYPE_CHECKING) # revealed: Literal[True]
reveal_type(TYPE_CHECKING) # revealed: bool

from stub import TYPE_CHECKING

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class _:
[reveal_type(a.z) for _ in range(1)] # revealed: Literal[0]

def _():
reveal_type(a.x) # revealed: Unknown | int | None
reveal_type(a.x) # revealed: int | None
reveal_type(a.y) # revealed: Unknown | None
reveal_type(a.z) # revealed: Unknown | None

Expand Down Expand Up @@ -75,7 +75,7 @@ class _:

if cond():
a = A()
reveal_type(a.x) # revealed: int | None | Unknown
reveal_type(a.x) # revealed: int | None
reveal_type(a.y) # revealed: Unknown | None
reveal_type(a.z) # revealed: Unknown | None

Expand Down Expand Up @@ -295,10 +295,10 @@ class C:

def _():
# error: [possibly-missing-attribute]
reveal_type(b.a.x[0]) # revealed: Unknown | int | None
reveal_type(b.a.x[0]) # revealed: int | None
# error: [possibly-missing-attribute]
reveal_type(b.a.x) # revealed: Unknown | list[int | None]
reveal_type(b.a) # revealed: Unknown | A | None
reveal_type(b.a.x) # revealed: list[int | None]
reveal_type(b.a) # revealed: A | None
```

## Invalid assignments are not used for narrowing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,11 @@ if c.x is not None:

if c.x is not None:
def _():
reveal_type(c.x) # revealed: Unknown | int | None
reveal_type(c.x) # revealed: int | None

def _():
if c.x is not None:
reveal_type(c.x) # revealed: (Unknown & ~None) | int
reveal_type(c.x) # revealed: int
```

## Subscript narrowing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class B:
reveal_type(a.x) # revealed: Literal["a"]

def f():
reveal_type(a.x) # revealed: Unknown | str | None
reveal_type(a.x) # revealed: str | None

[reveal_type(a.x) for _ in range(1)] # revealed: Literal["a"]

Expand All @@ -96,7 +96,7 @@ class C:
reveal_type(a.x) # revealed: str | None

def g():
reveal_type(a.x) # revealed: Unknown | str | None
reveal_type(a.x) # revealed: str | None

[reveal_type(a.x) for _ in range(1)] # revealed: str | None

Expand All @@ -109,7 +109,7 @@ class D:
reveal_type(a.x) # revealed: Literal["a"]

def h():
reveal_type(a.x) # revealed: Unknown | str | None
reveal_type(a.x) # revealed: str | None

# TODO: should be `str | None`
[reveal_type(a.x) for _ in range(1)] # revealed: Literal["a"]
Expand Down Expand Up @@ -190,7 +190,7 @@ def f(x: str | None):
reveal_type(g) # revealed: str

if a.x is not None:
reveal_type(a.x) # revealed: (Unknown & ~None) | str
reveal_type(a.x) # revealed: str

if l[0] is not None:
reveal_type(l[0]) # revealed: str
Expand All @@ -206,7 +206,7 @@ def f(x: str | None):
reveal_type(g) # revealed: str

if a.x is not None:
reveal_type(a.x) # revealed: (Unknown & ~None) | str
reveal_type(a.x) # revealed: str

if l[0] is not None:
reveal_type(l[0]) # revealed: str
Expand Down Expand Up @@ -382,12 +382,12 @@ def f():
if a.x is not None:
def _():
# Lazy nested scope narrowing is not performed on attributes/subscripts because it's difficult to track their changes.
reveal_type(a.x) # revealed: Unknown | str | None
reveal_type(a.x) # revealed: str | None

class D:
reveal_type(a.x) # revealed: (Unknown & ~None) | str
reveal_type(a.x) # revealed: str

[reveal_type(a.x) for _ in range(1)] # revealed: (Unknown & ~None) | str
[reveal_type(a.x) for _ in range(1)] # revealed: str

if l[0] is not None:
def _():
Expand Down Expand Up @@ -473,11 +473,11 @@ def f():
if a.x is not None:
def _():
if a.x != 1:
reveal_type(a.x) # revealed: (Unknown & ~Literal[1]) | str | None
reveal_type(a.x) # revealed: str | None

class D:
if a.x != 1:
reveal_type(a.x) # revealed: (Unknown & ~Literal[1] & ~None) | str
reveal_type(a.x) # revealed: str

if l[0] is not None:
def _():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ if flag():
x = 1

def f() -> None:
reveal_type(x) # revealed: Unknown | Literal[1, 2]
reveal_type(x) # revealed: int
# Function only used inside this branch
f()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ if flag():
chr: int = 1

def _():
# TODO: Should ideally be `Unknown | Literal[1] | (def abs(x: SupportsAbs[_T], /) -> _T)`
reveal_type(abs) # revealed: Unknown | Literal[1]
# TODO: Should ideally be `Literal[1] | (def abs(x: SupportsAbs[_T], /) -> _T)`
reveal_type(abs) # revealed: int
# TODO: Should ideally be `int | (def chr(i: SupportsIndex, /) -> str)`
reveal_type(chr) # revealed: int
```
6 changes: 3 additions & 3 deletions crates/ty_python_semantic/resources/mdtest/scopes/eager.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Function definitions are evaluated lazily.
x = 1

def f():
reveal_type(x) # revealed: Unknown | Literal[1, 2]
reveal_type(x) # revealed: int

x = 2
```
Expand Down Expand Up @@ -283,7 +283,7 @@ x = 1

def _():
class C:
# revealed: Unknown | Literal[1]
# revealed: int
[reveal_type(x) for _ in [1]]
x = 2
```
Expand Down Expand Up @@ -389,7 +389,7 @@ x = int
class C:
var: ClassVar[x]

reveal_type(C.var) # revealed: Unknown | int | str
reveal_type(C.var) # revealed: int | str

x = str
```
Expand Down
6 changes: 3 additions & 3 deletions crates/ty_python_semantic/resources/mdtest/scopes/global.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A name reference to a never-defined symbol in a function is implicitly a global
x = 1

def f():
reveal_type(x) # revealed: Unknown | Literal[1]
reveal_type(x) # revealed: int
```

## Explicit global in function
Expand All @@ -18,7 +18,7 @@ x = 1

def f():
global x
reveal_type(x) # revealed: Unknown | Literal[1]
reveal_type(x) # revealed: int
```

## Unassignable type in function
Expand Down Expand Up @@ -201,7 +201,7 @@ x = 42

def f():
global x
reveal_type(x) # revealed: Unknown | Literal[42]
reveal_type(x) # revealed: int
x = "56"
reveal_type(x) # revealed: Literal["56"]
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ __spec__ = 42 # error: [invalid-assignment] "Object of type `Literal[42]` is no
```py
import module

reveal_type(module.__file__) # revealed: Unknown | None
reveal_type(module.__file__) # revealed: None
reveal_type(module.__path__) # revealed: list[str]
reveal_type(module.__doc__) # revealed: Unknown
reveal_type(module.__spec__) # revealed: Unknown | ModuleSpec | None
reveal_type(module.__spec__) # revealed: ModuleSpec | None
# error: [unresolved-attribute]
reveal_type(module.__warningregistry__) # revealed: Unknown

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
x = 2

# error: [possibly-missing-attribute] "Attribute `x` on type `<class 'C'>` may be missing"
reveal_type(C.x) # revealed: Unknown | Literal[2]
reveal_type(C.x) # revealed: Unknown | int

Check failure on line 20 in crates/ty_python_semantic/resources/mdtest/scopes/unbound.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unexpected error: 13 [revealed-type] "Revealed type: `Unknown | Literal[2]`"

Check failure on line 20 in crates/ty_python_semantic/resources/mdtest/scopes/unbound.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unmatched assertion: revealed: Unknown | int
reveal_type(C.y) # revealed: Unknown | Literal[1]

Check failure on line 21 in crates/ty_python_semantic/resources/mdtest/scopes/unbound.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unexpected error: 13 [revealed-type] "Revealed type: `Unknown | int`"

Check failure on line 21 in crates/ty_python_semantic/resources/mdtest/scopes/unbound.md

View workflow job for this annotation

GitHub Actions / cargo test (linux)

unmatched assertion: revealed: Unknown | Literal[1]
```

## Possibly unbound in class and global scope
Expand All @@ -37,7 +37,7 @@
# Possibly unbound variables in enclosing scopes are considered bound.
y = x

reveal_type(C.y) # revealed: Unknown | Literal[1, "abc"]
reveal_type(C.y) # revealed: Unknown | Literal[1] | str
```

## Possibly unbound in class scope with multiple declarations
Expand Down
1 change: 0 additions & 1 deletion crates/ty_python_semantic/resources/primer/bad.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
spark # too many iterations (in `exported_names` query)
steam.py # hangs (single threaded)
6 changes: 5 additions & 1 deletion crates/ty_python_semantic/src/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,11 @@ fn place_by_id<'db>(
)
});

if scope.file(db).is_stub(db) || scope.scope(db).visibility().is_private() {
if scope.node(db).scope_kind().is_module() {
inferred
.map_type(|ty| ty.promote_literals(db, false))
.into()
} else if scope.file(db).is_stub(db) || scope.scope(db).visibility().is_private() {
// We generally trust module-level undeclared places in stubs and do not union
// with `Unknown`. If we don't do this, simple aliases like `IOError = OSError` in
// stubs would result in `IOError` being a union of `OSError` and `Unknown`, which
Expand Down
Loading
Loading