Skip to content

Commit 4e6f758

Browse files
committed
Merge branch 'main' into alex/star-imports-redux
2 parents bcb3ca9 + bb07ccd commit 4e6f758

File tree

48 files changed

+2910
-169
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2910
-169
lines changed

CHANGELOG.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,11 +1421,11 @@ The following rules have been stabilized and are no longer in preview:
14211421

14221422
The following behaviors have been stabilized:
14231423

1424-
- [`cancel-scope-no-checkpoint`](https://docs.astral.sh/ruff/rules/cancel-scope-no-checkpoint/) (`ASYNC100`): Support `asyncio` and `anyio` context mangers.
1425-
- [`async-function-with-timeout`](https://docs.astral.sh/ruff/rules/async-function-with-timeout/) (`ASYNC109`): Support `asyncio` and `anyio` context mangers.
1426-
- [`async-busy-wait`](https://docs.astral.sh/ruff/rules/async-busy-wait/) (`ASYNC110`): Support `asyncio` and `anyio` context mangers.
1427-
- [`async-zero-sleep`](https://docs.astral.sh/ruff/rules/async-zero-sleep/) (`ASYNC115`): Support `anyio` context mangers.
1428-
- [`long-sleep-not-forever`](https://docs.astral.sh/ruff/rules/long-sleep-not-forever/) (`ASYNC116`): Support `anyio` context mangers.
1424+
- [`cancel-scope-no-checkpoint`](https://docs.astral.sh/ruff/rules/cancel-scope-no-checkpoint/) (`ASYNC100`): Support `asyncio` and `anyio` context managers.
1425+
- [`async-function-with-timeout`](https://docs.astral.sh/ruff/rules/async-function-with-timeout/) (`ASYNC109`): Support `asyncio` and `anyio` context managers.
1426+
- [`async-busy-wait`](https://docs.astral.sh/ruff/rules/async-busy-wait/) (`ASYNC110`): Support `asyncio` and `anyio` context managers.
1427+
- [`async-zero-sleep`](https://docs.astral.sh/ruff/rules/async-zero-sleep/) (`ASYNC115`): Support `anyio` context managers.
1428+
- [`long-sleep-not-forever`](https://docs.astral.sh/ruff/rules/long-sleep-not-forever/) (`ASYNC116`): Support `anyio` context managers.
14291429

14301430
The following fixes have been stabilized:
14311431

crates/red_knot_python_semantic/resources/mdtest/annotations/callable.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,21 @@ def _(c: Callable[[int, str], int]):
152152
reveal_type(c) # revealed: (int, str, /) -> int
153153
```
154154

155+
## Union
156+
157+
```py
158+
from typing import Callable, Union
159+
160+
def _(
161+
c: Callable[[Union[int, str]], int] | None,
162+
d: None | Callable[[Union[int, str]], int],
163+
e: None | Callable[[Union[int, str]], int] | int,
164+
):
165+
reveal_type(c) # revealed: ((int | str, /) -> int) | None
166+
reveal_type(d) # revealed: None | ((int | str, /) -> int)
167+
reveal_type(e) # revealed: None | ((int | str, /) -> int) | int
168+
```
169+
155170
## Nested
156171

157172
A nested `Callable` as one of the parameter types:

crates/red_knot_python_semantic/resources/mdtest/annotations/unsupported_special_forms.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ def i(callback: Callable[Concatenate[int, P], R_co], *args: P.args, **kwargs: P.
2929
# TODO: should understand the annotation
3030
reveal_type(kwargs) # revealed: dict
3131

32-
# TODO: not an error; remove once `call` is implemented for `Callable`
33-
# error: [call-non-callable]
3432
return callback(42, *args, **kwargs)
3533

3634
class Foo:
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# `typing.Callable`
2+
3+
```py
4+
from typing import Callable
5+
6+
def _(c: Callable[[], int]):
7+
reveal_type(c()) # revealed: int
8+
9+
def _(c: Callable[[int, str], int]):
10+
reveal_type(c(1, "a")) # revealed: int
11+
12+
# error: [invalid-argument-type] "Object of type `Literal["a"]` cannot be assigned to parameter 1; expected type `int`"
13+
# error: [invalid-argument-type] "Object of type `Literal[1]` cannot be assigned to parameter 2; expected type `str`"
14+
reveal_type(c("a", 1)) # revealed: int
15+
```
16+
17+
The `Callable` annotation can only be used to describe positional-only parameters.
18+
19+
```py
20+
def _(c: Callable[[int, str], None]):
21+
# error: [unknown-argument] "Argument `a` does not match any known parameter"
22+
# error: [unknown-argument] "Argument `b` does not match any known parameter"
23+
# error: [missing-argument] "No arguments provided for required parameters 1, 2"
24+
reveal_type(c(a=1, b="b")) # revealed: None
25+
```
26+
27+
If the annotation uses a gradual form (`...`) for the parameter list, then it can accept any kind of
28+
parameter with any type.
29+
30+
```py
31+
def _(c: Callable[..., int]):
32+
reveal_type(c()) # revealed: int
33+
reveal_type(c(1)) # revealed: int
34+
reveal_type(c(1, "str", False, a=[1, 2], b=(3, 4))) # revealed: int
35+
```
36+
37+
An invalid `Callable` form can accept any parameters and will return `Unknown`.
38+
39+
```py
40+
# error: [invalid-type-form]
41+
def _(c: Callable[42, str]):
42+
reveal_type(c()) # revealed: Unknown
43+
```

crates/red_knot_python_semantic/resources/mdtest/exception/except_star.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ try:
2424
help()
2525
except* OSError as e:
2626
# TODO: more precise would be `ExceptionGroup[OSError]` --Alex
27-
# (needs homogenous tuples + generics)
27+
# (needs homogeneous tuples + generics)
2828
reveal_type(e) # revealed: BaseExceptionGroup
2929
```
3030

@@ -35,7 +35,7 @@ try:
3535
help()
3636
except* (TypeError, AttributeError) as e:
3737
# TODO: more precise would be `ExceptionGroup[TypeError | AttributeError]` --Alex
38-
# (needs homogenous tuples + generics)
38+
# (needs homogeneous tuples + generics)
3939
reveal_type(e) # revealed: BaseExceptionGroup
4040
```
4141

crates/red_knot_python_semantic/resources/mdtest/expression/lambda.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Using a parameter with default value:
7676
lambda x=1: reveal_type(x) # revealed: Unknown | Literal[1]
7777
```
7878

79-
Using a variadic paramter:
79+
Using a variadic parameter:
8080

8181
```py
8282
# TODO: should be `tuple[Unknown, ...]` (needs generics)
@@ -98,3 +98,22 @@ expression.
9898
```py
9999
reveal_type(lambda a=lambda x, y: 0: 2) # revealed: (a=(x, y) -> Unknown) -> Unknown
100100
```
101+
102+
## Assignment
103+
104+
This does not enumerate all combinations of parameter kinds as that should be covered by the
105+
[subtype tests for callable types](./../type_properties/is_subtype_of.md#callable).
106+
107+
```py
108+
from typing import Callable
109+
110+
a1: Callable[[], None] = lambda: None
111+
a2: Callable[[int], None] = lambda x: None
112+
a3: Callable[[int, int], None] = lambda x, y, z=1: None
113+
a4: Callable[[int, int], None] = lambda *args: None
114+
115+
# error: [invalid-assignment]
116+
a5: Callable[[], None] = lambda x: None
117+
# error: [invalid-assignment]
118+
a6: Callable[[int], None] = lambda: None
119+
```

crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,87 @@ static_assert(is_assignable_to(Never, type[str]))
393393
static_assert(is_assignable_to(Never, type[Any]))
394394
```
395395

396+
## Callable
397+
398+
The examples provided below are only a subset of the possible cases and include the ones with
399+
gradual types. The cases with fully static types and using different combinations of parameter kinds
400+
are covered in the [subtyping tests](./is_subtype_of.md#callable).
401+
402+
### Return type
403+
404+
```py
405+
from knot_extensions import CallableTypeFromFunction, Unknown, static_assert, is_assignable_to
406+
from typing import Any, Callable
407+
408+
static_assert(is_assignable_to(Callable[[], Any], Callable[[], int]))
409+
static_assert(is_assignable_to(Callable[[], int], Callable[[], Any]))
410+
411+
static_assert(is_assignable_to(Callable[[], int], Callable[[], float]))
412+
static_assert(not is_assignable_to(Callable[[], float], Callable[[], int]))
413+
```
414+
415+
The return types should be checked even if the parameter types uses gradual form (`...`).
416+
417+
```py
418+
static_assert(is_assignable_to(Callable[..., int], Callable[..., float]))
419+
static_assert(not is_assignable_to(Callable[..., float], Callable[..., int]))
420+
```
421+
422+
And, if there is no return type, the return type is `Unknown`.
423+
424+
```py
425+
static_assert(is_assignable_to(Callable[[], Unknown], Callable[[], int]))
426+
static_assert(is_assignable_to(Callable[[], int], Callable[[], Unknown]))
427+
```
428+
429+
### Parameter types
430+
431+
A `Callable` which uses the gradual form (`...`) for the parameter types is consistent with any
432+
input signature.
433+
434+
```py
435+
from knot_extensions import CallableTypeFromFunction, static_assert, is_assignable_to
436+
from typing import Any, Callable
437+
438+
static_assert(is_assignable_to(Callable[[], None], Callable[..., None]))
439+
static_assert(is_assignable_to(Callable[..., None], Callable[..., None]))
440+
static_assert(is_assignable_to(Callable[[int, float, str], None], Callable[..., None]))
441+
```
442+
443+
Even if it includes any other parameter kinds.
444+
445+
```py
446+
def positional_only(a: int, b: int, /) -> None: ...
447+
def positional_or_keyword(a: int, b: int) -> None: ...
448+
def variadic(*args: int) -> None: ...
449+
def keyword_only(*, a: int, b: int) -> None: ...
450+
def keyword_variadic(**kwargs: int) -> None: ...
451+
def mixed(a: int, /, b: int, *args: int, c: int, **kwargs: int) -> None: ...
452+
453+
static_assert(is_assignable_to(CallableTypeFromFunction[positional_only], Callable[..., None]))
454+
static_assert(is_assignable_to(CallableTypeFromFunction[positional_or_keyword], Callable[..., None]))
455+
static_assert(is_assignable_to(CallableTypeFromFunction[variadic], Callable[..., None]))
456+
static_assert(is_assignable_to(CallableTypeFromFunction[keyword_only], Callable[..., None]))
457+
static_assert(is_assignable_to(CallableTypeFromFunction[keyword_variadic], Callable[..., None]))
458+
static_assert(is_assignable_to(CallableTypeFromFunction[mixed], Callable[..., None]))
459+
```
460+
461+
And, even if the parameters are unannotated.
462+
463+
```py
464+
def positional_only(a, b, /) -> None: ...
465+
def positional_or_keyword(a, b) -> None: ...
466+
def variadic(*args) -> None: ...
467+
def keyword_only(*, a, b) -> None: ...
468+
def keyword_variadic(**kwargs) -> None: ...
469+
def mixed(a, /, b, *args, c, **kwargs) -> None: ...
470+
471+
static_assert(is_assignable_to(CallableTypeFromFunction[positional_only], Callable[..., None]))
472+
static_assert(is_assignable_to(CallableTypeFromFunction[positional_or_keyword], Callable[..., None]))
473+
static_assert(is_assignable_to(CallableTypeFromFunction[variadic], Callable[..., None]))
474+
static_assert(is_assignable_to(CallableTypeFromFunction[keyword_only], Callable[..., None]))
475+
static_assert(is_assignable_to(CallableTypeFromFunction[keyword_variadic], Callable[..., None]))
476+
static_assert(is_assignable_to(CallableTypeFromFunction[mixed], Callable[..., None]))
477+
```
478+
396479
[typing documentation]: https://typing.readthedocs.io/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation

crates/red_knot_python_semantic/src/ast_node_ref.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use ruff_db::parsed::ParsedModule;
1717
/// ## Usage in salsa tracked structs
1818
/// It's important that [`AstNodeRef`] fields in salsa tracked structs are tracked fields
1919
/// (attributed with `#[tracked`]). It prevents that the tracked struct gets a new ID
20-
/// everytime the AST changes, which in turn, invalidates the result of any query
20+
/// every time the AST changes, which in turn, invalidates the result of any query
2121
/// that takes said tracked struct as a query argument or returns the tracked struct as part of its result.
2222
///
2323
/// For example, marking the [`AstNodeRef`] as tracked on `Expression`

crates/red_knot_python_semantic/src/module_resolver/resolver.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ impl SearchPaths {
224224

225225
let site_packages_paths = match python_path {
226226
PythonPath::SysPrefix(sys_prefix, origin) => {
227+
tracing::debug!(
228+
"Discovering site-packages paths from sys-prefix `{sys_prefix}` ({origin}')"
229+
);
227230
// TODO: We may want to warn here if the venv's python version is older
228231
// than the one resolved in the program settings because it indicates
229232
// that the `target-version` is incorrectly configured or that the

crates/red_knot_python_semantic/src/semantic_index/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ where
961961

962962
self.with_type_params(
963963
NodeWithScopeRef::TypeAliasTypeParameters(type_alias),
964-
type_alias.type_params.as_ref(),
964+
type_alias.type_params.as_deref(),
965965
|builder| {
966966
builder.push_scope(NodeWithScopeRef::TypeAlias(type_alias));
967967
builder.visit_expr(&type_alias.value);

0 commit comments

Comments
 (0)