diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/deferred.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/deferred.md index 729edcb6a0..c12edcec1a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/deferred.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/deferred.md @@ -2,7 +2,9 @@ ## Deferred annotations in stubs always resolve -```pyi path=mod.pyi +`mod.pyi`: + +```pyi def get_foo() -> Foo: ... class Foo: ... ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md index 360530efc8..33d90fafab 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md @@ -116,7 +116,9 @@ def union_example( Only Literal that is defined in typing and typing_extension modules is detected as the special Literal. -```pyi path=other.pyi +`other.pyi`: + +```pyi from typing import _SpecialForm Literal: _SpecialForm diff --git a/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md b/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md index c1fee047fe..2169e01e5e 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md +++ b/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md @@ -25,7 +25,9 @@ x = "foo" # error: [invalid-assignment] "Object of type `Literal["foo"]` is not ## Tuple annotations are understood -```py path=module.py +`module.py`: + +```py from typing_extensions import Unpack a: tuple[()] = () @@ -40,7 +42,9 @@ i: tuple[str | int, str | int] = (42, 42) j: tuple[str | int] = (42,) ``` -```py path=script.py +`script.py`: + +```py from module import a, b, c, d, e, f, g, h, i, j reveal_type(a) # revealed: tuple[()] @@ -114,7 +118,7 @@ reveal_type(x) # revealed: Foo ## Annotations in stub files are deferred -```pyi path=main.pyi +```pyi x: Foo class Foo: ... @@ -125,7 +129,7 @@ reveal_type(x) # revealed: Foo ## Annotated assignments in stub files are inferred correctly -```pyi path=main.pyi +```pyi x: int = 1 reveal_type(x) # revealed: Literal[1] ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/attributes.md b/crates/red_knot_python_semantic/resources/mdtest/attributes.md index 9b13af91eb..17da6541ff 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/attributes.md +++ b/crates/red_knot_python_semantic/resources/mdtest/attributes.md @@ -703,7 +703,9 @@ reveal_type(Foo.__class__) # revealed: Literal[type] ## Module attributes -```py path=mod.py +`mod.py`: + +```py global_symbol: str = "a" ``` @@ -737,13 +739,19 @@ for mod.global_symbol in IntIterable(): ## Nested attributes -```py path=outer/__init__.py +`outer/__init__.py`: + +```py ``` -```py path=outer/nested/__init__.py +`outer/nested/__init__.py`: + +```py ``` -```py path=outer/nested/inner.py +`outer/nested/inner.py`: + +```py class Outer: class Nested: class Inner: @@ -766,7 +774,9 @@ outer.nested.inner.Outer.Nested.Inner.attr = "a" Most attribute accesses on function-literal types are delegated to `types.FunctionType`, since all functions are instances of that class: -```py path=a.py +`a.py`: + +```py def f(): ... reveal_type(f.__defaults__) # revealed: @Todo(full tuple[...] support) | None @@ -775,7 +785,9 @@ reveal_type(f.__kwdefaults__) # revealed: @Todo(generics) | None Some attributes are special-cased, however: -```py path=b.py +`b.py`: + +```py def f(): ... reveal_type(f.__get__) # revealed: @Todo(`__get__` method on functions) @@ -787,14 +799,18 @@ reveal_type(f.__call__) # revealed: @Todo(`__call__` method on functions) Most attribute accesses on int-literal types are delegated to `builtins.int`, since all literal integers are instances of that class: -```py path=a.py +`a.py`: + +```py reveal_type((2).bit_length) # revealed: @Todo(bound method) reveal_type((2).denominator) # revealed: @Todo(@property) ``` Some attributes are special-cased, however: -```py path=b.py +`b.py`: + +```py reveal_type((2).numerator) # revealed: Literal[2] reveal_type((2).real) # revealed: Literal[2] ``` @@ -804,14 +820,18 @@ reveal_type((2).real) # revealed: Literal[2] Most attribute accesses on bool-literal types are delegated to `builtins.bool`, since all literal bols are instances of that class: -```py path=a.py +`a.py`: + +```py reveal_type(True.__and__) # revealed: @Todo(bound method) reveal_type(False.__or__) # revealed: @Todo(bound method) ``` Some attributes are special-cased, however: -```py path=b.py +`b.py`: + +```py reveal_type(True.numerator) # revealed: Literal[1] reveal_type(False.real) # revealed: Literal[0] ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md b/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md index 7ad8c991c6..d9d42f7d0a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md +++ b/crates/red_knot_python_semantic/resources/mdtest/boundness_declaredness/public.md @@ -36,7 +36,9 @@ In particular, we should raise errors in the "possibly-undeclared-and-unbound" a If a symbol has a declared type (`int`), we use that even if there is a more precise inferred type (`Literal[1]`), or a conflicting inferred type (`str` vs. `Literal[2]` below): -```py path=mod.py +`mod.py`: + +```py from typing import Any def any() -> Any: ... @@ -61,7 +63,9 @@ reveal_type(d) # revealed: int If a symbol is declared and *possibly* unbound, we trust that other module and use the declared type without raising an error. -```py path=mod.py +`mod.py`: + +```py from typing import Any def any() -> Any: ... @@ -93,7 +97,9 @@ reveal_type(d) # revealed: int Similarly, if a symbol is declared but unbound, we do not raise an error. We trust that this symbol is available somehow and simply use the declared type. -```py path=mod.py +`mod.py`: + +```py from typing import Any a: int @@ -114,7 +120,9 @@ reveal_type(b) # revealed: Any If a symbol is possibly undeclared but definitely bound, we use the union of the declared and inferred types: -```py path=mod.py +`mod.py`: + +```py from typing import Any def any() -> Any: ... @@ -151,7 +159,9 @@ inferred types. This case is interesting because the "possibly declared" definit same as the "possibly bound" definition (symbol `b`). Note that we raise a `possibly-unbound-import` error for both `a` and `b`: -```py path=mod.py +`mod.py`: + +```py from typing import Any def flag() -> bool: ... @@ -181,7 +191,9 @@ b = None If a symbol is possibly undeclared and definitely unbound, we currently do not raise an error. This seems inconsistent when compared to the case just above. -```py path=mod.py +`mod.py`: + +```py def flag() -> bool: ... if flag(): @@ -208,7 +220,9 @@ If a symbol is *undeclared*, we use the union of `Unknown` with the inferred typ treat this case differently from the case where a symbol is implicitly declared with `Unknown`, possibly due to the usage of an unknown name in the annotation: -```py path=mod.py +`mod.py`: + +```py # Undeclared: a = 1 @@ -231,7 +245,9 @@ a = None If a symbol is undeclared and *possibly* unbound, we currently do not raise an error. This seems inconsistent when compared to the "possibly-undeclared-and-possibly-unbound" case. -```py path=mod.py +`mod.py`: + +```py def flag() -> bool: ... if flag: @@ -255,7 +271,9 @@ a = None If a symbol is undeclared *and* unbound, we infer `Unknown` and raise an error. -```py path=mod.py +`mod.py`: + +```py if False: a: int = 1 ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.md b/crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.md index 8fe7f29541..805fb39e3a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.md @@ -33,7 +33,9 @@ reveal_type(a >= b) # revealed: Literal[False] Even when tuples have different lengths, comparisons should be handled appropriately. -```py path=different_length.py +`different_length.py`: + +```py a = (1, 2, 3) b = (1, 2, 3, 4) @@ -102,7 +104,9 @@ reveal_type(a >= b) # revealed: bool However, if the lexicographic comparison completes without reaching a point where str and int are compared, Python will still produce a result based on the prior elements. -```py path=short_circuit.py +`short_circuit.py`: + +```py a = (1, 2) b = (999999, "hello") diff --git a/crates/red_knot_python_semantic/resources/mdtest/exception/control_flow.md b/crates/red_knot_python_semantic/resources/mdtest/exception/control_flow.md index 284b0f24d5..ab49dda658 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/exception/control_flow.md +++ b/crates/red_knot_python_semantic/resources/mdtest/exception/control_flow.md @@ -29,7 +29,9 @@ completing. The type of `x` at the beginning of the `except` suite in this examp `x = could_raise_returns_str()` redefinition, but we *also* could have jumped to the `except` suite *after* that redefinition. -```py path=union_type_inferred.py +`union_type_inferred.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -50,7 +52,9 @@ reveal_type(x) # revealed: str | Literal[2] If `x` has the same type at the end of both branches, however, the branches unify and `x` is not inferred as having a union type following the `try`/`except` block: -```py path=branches_unify_to_non_union_type.py +`branches_unify_to_non_union_type.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -133,7 +137,9 @@ the `except` suite: - At the end of `else`, `x == 3` - At the end of `except`, `x == 2` -```py path=single_except.py +`single_except.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -192,7 +198,9 @@ A `finally` suite is *always* executed. As such, if we reach the `reveal_type` c this example, we know that `x` *must* have been reassigned to `2` during the `finally` suite. The type of `x` at the end of the example is therefore `Literal[2]`: -```py path=redef_in_finally.py +`redef_in_finally.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -217,7 +225,9 @@ at this point than there were when we were inside the `finally` block. (Our current model does *not* correctly infer the types *inside* `finally` suites, however; this is still a TODO item for us.) -```py path=no_redef_in_finally.py +`no_redef_in_finally.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -249,7 +259,9 @@ suites: exception raised in the `except` suite to cause us to jump to the `finally` suite before the `except` suite ran to completion -```py path=redef_in_finally.py +`redef_in_finally.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -286,7 +298,9 @@ itself. (In some control-flow possibilities, some exceptions were merely *suspen `finally` suite; these lead to the scope's termination following the conclusion of the `finally` suite.) -```py path=no_redef_in_finally.py +`no_redef_in_finally.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -317,7 +331,9 @@ reveal_type(x) # revealed: str | bool An example with multiple `except` branches and a `finally` branch: -```py path=multiple_except_branches.py +`multiple_except_branches.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -364,7 +380,9 @@ If the exception handler has an `else` branch, we must also take into account th control flow could have jumped to the `finally` suite from partway through the `else` suite due to an exception raised *there*. -```py path=single_except_branch.py +`single_except_branch.py`: + +```py def could_raise_returns_str() -> str: return "foo" @@ -407,7 +425,9 @@ reveal_type(x) # revealed: bool | float The same again, this time with multiple `except` branches: -```py path=multiple_except_branches.py +`multiple_except_branches.py`: + +```py def could_raise_returns_str() -> str: return "foo" diff --git a/crates/red_knot_python_semantic/resources/mdtest/expression/boolean.md b/crates/red_knot_python_semantic/resources/mdtest/expression/boolean.md index 8231f44b06..22fea2c5c4 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/expression/boolean.md +++ b/crates/red_knot_python_semantic/resources/mdtest/expression/boolean.md @@ -54,7 +54,9 @@ reveal_type("x" or "y" and "") # revealed: Literal["x"] ## Evaluates to builtin -```py path=a.py +`a.py`: + +```py redefined_builtin_bool: type[bool] = bool def my_bool(x) -> bool: diff --git a/crates/red_knot_python_semantic/resources/mdtest/generics.md b/crates/red_knot_python_semantic/resources/mdtest/generics.md index 24459f8feb..dd40ccbaf9 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/generics.md +++ b/crates/red_knot_python_semantic/resources/mdtest/generics.md @@ -51,7 +51,7 @@ In type stubs, classes can reference themselves in their base class definitions. This should hold true even with generics at play. -```py path=a.pyi +```pyi class Seq[T]: ... # TODO not error on the subscripting diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/basic.md b/crates/red_knot_python_semantic/resources/mdtest/import/basic.md index 176f09f6e0..44f5f078bb 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/basic.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/basic.md @@ -9,7 +9,9 @@ E = D reveal_type(E) # revealed: Literal[C] ``` -```py path=b.py +`b.py`: + +```py class C: ... ``` @@ -22,7 +24,9 @@ D = b.C reveal_type(D) # revealed: Literal[C] ``` -```py path=b.py +`b.py`: + +```py class C: ... ``` @@ -34,10 +38,14 @@ import a.b reveal_type(a.b.C) # revealed: Literal[C] ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b.py +`a/b.py`: + +```py class C: ... ``` @@ -49,13 +57,19 @@ import a.b.c reveal_type(a.b.c.C) # revealed: Literal[C] ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b/__init__.py +`a/b/__init__.py`: + +```py ``` -```py path=a/b/c.py +`a/b/c.py`: + +```py class C: ... ``` @@ -67,10 +81,14 @@ import a.b as b reveal_type(b.C) # revealed: Literal[C] ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b.py +`a/b.py`: + +```py class C: ... ``` @@ -82,13 +100,19 @@ import a.b.c as c reveal_type(c.C) # revealed: Literal[C] ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b/__init__.py +`a/b/__init__.py`: + +```py ``` -```py path=a/b/c.py +`a/b/c.py`: + +```py class C: ... ``` @@ -102,5 +126,7 @@ import a.foo # error: [unresolved-import] "Cannot resolve import `a.foo`" import b.foo # error: [unresolved-import] "Cannot resolve import `b.foo`" ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/builtins.md b/crates/red_knot_python_semantic/resources/mdtest/import/builtins.md index 411c1940fb..5a1af24fe7 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/builtins.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/builtins.md @@ -29,13 +29,17 @@ builtins from the "actual" vendored typeshed: typeshed = "/typeshed" ``` -```pyi path=/typeshed/stdlib/builtins.pyi +`/typeshed/stdlib/builtins.pyi`: + +```pyi class Custom: ... custom_builtin: Custom ``` -```pyi path=/typeshed/stdlib/typing_extensions.pyi +`/typeshed/stdlib/typing_extensions.pyi`: + +```pyi def reveal_type(obj, /): ... ``` @@ -56,12 +60,16 @@ that point: typeshed = "/typeshed" ``` -```pyi path=/typeshed/stdlib/builtins.pyi +`/typeshed/stdlib/builtins.pyi`: + +```pyi foo = bar bar = 1 ``` -```pyi path=/typeshed/stdlib/typing_extensions.pyi +`/typeshed/stdlib/typing_extensions.pyi`: + +```pyi def reveal_type(obj, /): ... ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/conditional.md b/crates/red_knot_python_semantic/resources/mdtest/import/conditional.md index e4c2460648..26e849666f 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/conditional.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/conditional.md @@ -2,7 +2,9 @@ ## Maybe unbound -```py path=maybe_unbound.py +`maybe_unbound.py`: + +```py def coinflip() -> bool: return True @@ -29,7 +31,9 @@ reveal_type(y) # revealed: Unknown | Literal[3] ## Maybe unbound annotated -```py path=maybe_unbound_annotated.py +`maybe_unbound_annotated.py`: + +```py def coinflip() -> bool: return True @@ -60,7 +64,9 @@ reveal_type(y) # revealed: int Importing a possibly undeclared name still gives us its declared type: -```py path=maybe_undeclared.py +`maybe_undeclared.py`: + +```py def coinflip() -> bool: return True @@ -76,11 +82,15 @@ reveal_type(x) # revealed: int ## Reimport -```py path=c.py +`c.py`: + +```py def f(): ... ``` -```py path=b.py +`b.py`: + +```py def coinflip() -> bool: return True @@ -102,11 +112,15 @@ reveal_type(f) # revealed: Literal[f, f] When we have a declared type in one path and only an inferred-from-definition type in the other, we should still be able to unify those: -```py path=c.pyi +`c.pyi`: + +```pyi x: int ``` -```py path=b.py +`b.py`: + +```py def coinflip() -> bool: return True diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/conflicts.md b/crates/red_knot_python_semantic/resources/mdtest/import/conflicts.md index e70ec8bb46..353166220f 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/conflicts.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/conflicts.md @@ -8,11 +8,15 @@ import a.b reveal_type(a.b) # revealed: ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py b: int = 42 ``` -```py path=a/b.py +`a/b.py`: + +```py ``` ## Via from/import @@ -23,11 +27,15 @@ from a import b reveal_type(b) # revealed: int ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py b: int = 42 ``` -```py path=a/b.py +`a/b.py`: + +```py ``` ## Via both @@ -40,11 +48,15 @@ reveal_type(b) # revealed: reveal_type(a.b) # revealed: ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py b: int = 42 ``` -```py path=a/b.py +`a/b.py`: + +```py ``` ## Via both (backwards) @@ -65,11 +77,15 @@ reveal_type(b) # revealed: reveal_type(a.b) # revealed: ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py b: int = 42 ``` -```py path=a/b.py +`a/b.py`: + +```py ``` [from-import]: https://docs.python.org/3/reference/simple_stmts.html#the-import-statement diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/errors.md b/crates/red_knot_python_semantic/resources/mdtest/import/errors.md index 6c30a92021..22e747d351 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/errors.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/errors.md @@ -18,7 +18,9 @@ reveal_type(baz) # revealed: Unknown ## Unresolved import from resolved module -```py path=a.py +`a.py`: + +```py ``` ```py @@ -29,7 +31,9 @@ reveal_type(thing) # revealed: Unknown ## Resolved import of symbol from unresolved import -```py path=a.py +`a.py`: + +```py import foo as foo # error: "Cannot resolve import `foo`" reveal_type(foo) # revealed: Unknown @@ -46,7 +50,9 @@ reveal_type(foo) # revealed: Unknown ## No implicit shadowing -```py path=b.py +`b.py`: + +```py x: int ``` @@ -58,7 +64,9 @@ x = "foo" # error: [invalid-assignment] "Object of type `Literal["foo"]" ## Import cycle -```py path=a.py +`a.py`: + +```py class A: ... reveal_type(A.__mro__) # revealed: tuple[Literal[A], Literal[object]] @@ -69,7 +77,9 @@ class C(b.B): ... reveal_type(C.__mro__) # revealed: tuple[Literal[C], Literal[B], Literal[A], Literal[object]] ``` -```py path=b.py +`b.py`: + +```py from a import A class B(A): ... diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/invalid_syntax.md b/crates/red_knot_python_semantic/resources/mdtest/import/invalid_syntax.md index 727f8c157d..6b7423f86d 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/invalid_syntax.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/invalid_syntax.md @@ -23,9 +23,13 @@ reveal_type(b) # revealed: reveal_type(b.c) # revealed: int ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b.py +`a/b.py`: + +```py c: int = 1 ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/relative.md b/crates/red_knot_python_semantic/resources/mdtest/import/relative.md index 2ebd48cdb0..cc34cbaf3e 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/relative.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/relative.md @@ -2,10 +2,14 @@ ## Non-existent -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/bar.py +`package/bar.py`: + +```py from .foo import X # error: [unresolved-import] reveal_type(X) # revealed: Unknown @@ -13,14 +17,20 @@ reveal_type(X) # revealed: Unknown ## Simple -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/foo.py +`package/foo.py`: + +```py X: int = 42 ``` -```py path=package/bar.py +`package/bar.py`: + +```py from .foo import X reveal_type(X) # revealed: int @@ -28,14 +38,20 @@ reveal_type(X) # revealed: int ## Dotted -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/foo/bar/baz.py +`package/foo/bar/baz.py`: + +```py X: int = 42 ``` -```py path=package/bar.py +`package/bar.py`: + +```py from .foo.bar.baz import X reveal_type(X) # revealed: int @@ -43,11 +59,15 @@ reveal_type(X) # revealed: int ## Bare to package -```py path=package/__init__.py +`package/__init__.py`: + +```py X: int = 42 ``` -```py path=package/bar.py +`package/bar.py`: + +```py from . import X reveal_type(X) # revealed: int @@ -55,7 +75,9 @@ reveal_type(X) # revealed: int ## Non-existent + bare to package -```py path=package/bar.py +`package/bar.py`: + +```py from . import X # error: [unresolved-import] reveal_type(X) # revealed: Unknown @@ -63,19 +85,25 @@ reveal_type(X) # revealed: Unknown ## Dunder init -```py path=package/__init__.py +`package/__init__.py`: + +```py from .foo import X reveal_type(X) # revealed: int ``` -```py path=package/foo.py +`package/foo.py`: + +```py X: int = 42 ``` ## Non-existent + dunder init -```py path=package/__init__.py +`package/__init__.py`: + +```py from .foo import X # error: [unresolved-import] reveal_type(X) # revealed: Unknown @@ -83,14 +111,20 @@ reveal_type(X) # revealed: Unknown ## Long relative import -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/foo.py +`package/foo.py`: + +```py X: int = 42 ``` -```py path=package/subpackage/subsubpackage/bar.py +`package/subpackage/subsubpackage/bar.py`: + +```py from ...foo import X reveal_type(X) # revealed: int @@ -98,14 +132,20 @@ reveal_type(X) # revealed: int ## Unbound symbol -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/foo.py +`package/foo.py`: + +```py x # error: [unresolved-reference] ``` -```py path=package/bar.py +`package/bar.py`: + +```py from .foo import x # error: [unresolved-import] reveal_type(x) # revealed: Unknown @@ -113,14 +153,20 @@ reveal_type(x) # revealed: Unknown ## Bare to module -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/foo.py +`package/foo.py`: + +```py X: int = 42 ``` -```py path=package/bar.py +`package/bar.py`: + +```py from . import foo reveal_type(foo.X) # revealed: int @@ -131,10 +177,14 @@ reveal_type(foo.X) # revealed: int This test verifies that we emit an error when we try to import a symbol that is neither a submodule nor an attribute of `package`. -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/bar.py +`package/bar.py`: + +```py from . import foo # error: [unresolved-import] reveal_type(foo) # revealed: Unknown @@ -148,14 +198,20 @@ submodule when that submodule name appears in the `imported_modules` set. That m that are imported via `from...import` are not visible to our type inference if you also access that submodule via the attribute on its parent package. -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/foo.py +`package/foo.py`: + +```py X: int = 42 ``` -```py path=package/bar.py +`package/bar.py`: + +```py from . import foo import package diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/stubs.md b/crates/red_knot_python_semantic/resources/mdtest/import/stubs.md index 151b9cc110..6a614ed557 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/stubs.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/stubs.md @@ -9,7 +9,9 @@ y = x reveal_type(y) # revealed: int ``` -```py path=b.pyi +`b.pyi`: + +```pyi x: int ``` @@ -22,6 +24,8 @@ y = x reveal_type(y) # revealed: int ``` -```py path=b.py +`b.py`: + +```py x: int = 1 ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/import/tracking.md b/crates/red_knot_python_semantic/resources/mdtest/import/tracking.md index 6d10126c77..effbcd4a98 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/import/tracking.md +++ b/crates/red_knot_python_semantic/resources/mdtest/import/tracking.md @@ -32,10 +32,14 @@ reveal_type(a.b.C) # revealed: Literal[C] import a.b ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b.py +`a/b.py`: + +```py class C: ... ``` @@ -55,14 +59,20 @@ reveal_type(a.b) # revealed: reveal_type(a.b.C) # revealed: Literal[C] ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b.py +`a/b.py`: + +```py class C: ... ``` -```py path=q.py +`q.py`: + +```py import a as a import a.b as b ``` @@ -83,18 +93,26 @@ reveal_type(sub.b) # revealed: reveal_type(attr.b) # revealed: ``` -```py path=sub/__init__.py +`sub/__init__.py`: + +```py b = 1 ``` -```py path=sub/b.py +`sub/b.py`: + +```py ``` -```py path=attr/__init__.py +`attr/__init__.py`: + +```py from . import b as _ b = 1 ``` -```py path=attr/b.py +`attr/b.py`: + +```py ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/known_constants.md b/crates/red_knot_python_semantic/resources/mdtest/known_constants.md index ddad1fcc75..b25de94d96 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/known_constants.md +++ b/crates/red_knot_python_semantic/resources/mdtest/known_constants.md @@ -31,7 +31,9 @@ reveal_type(TC) # revealed: Literal[True] Make sure we only use our special handling for `typing.TYPE_CHECKING` and not for other constants with the same name: -```py path=constants.py +`constants.py`: + +```py TYPE_CHECKING: bool = False ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/mdtest_custom_typeshed.md b/crates/red_knot_python_semantic/resources/mdtest/mdtest_custom_typeshed.md index e2481d8c2d..e4255708fe 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/mdtest_custom_typeshed.md +++ b/crates/red_knot_python_semantic/resources/mdtest/mdtest_custom_typeshed.md @@ -19,13 +19,17 @@ typeshed = "/typeshed" We can then place custom stub files in `/typeshed/stdlib`, for example: -```pyi path=/typeshed/stdlib/builtins.pyi +`/typeshed/stdlib/builtins.pyi`: + +```pyi class BuiltinClass: ... builtin_symbol: BuiltinClass ``` -```pyi path=/typeshed/stdlib/sys/__init__.pyi +`/typeshed/stdlib/sys/__init__.pyi`: + +```pyi version = "my custom Python" ``` @@ -54,15 +58,21 @@ python-version = "3.10" typeshed = "/typeshed" ``` -```pyi path=/typeshed/stdlib/old_module.pyi +`/typeshed/stdlib/old_module.pyi`: + +```pyi class OldClass: ... ``` -```pyi path=/typeshed/stdlib/new_module.pyi +`/typeshed/stdlib/new_module.pyi`: + +```pyi class NewClass: ... ``` -```text path=/typeshed/stdlib/VERSIONS +`/typeshed/stdlib/VERSIONS`: + +```text old_module: 3.0- new_module: 3.11- ``` @@ -86,7 +96,9 @@ simple untyped definition is enough to make `reveal_type` work in tests: typeshed = "/typeshed" ``` -```pyi path=/typeshed/stdlib/typing_extensions.pyi +`/typeshed/stdlib/typing_extensions.pyi`: + +```pyi def reveal_type(obj, /): ... ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/metaclass.md b/crates/red_knot_python_semantic/resources/mdtest/metaclass.md index 09687abfe2..92dc835e45 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/metaclass.md +++ b/crates/red_knot_python_semantic/resources/mdtest/metaclass.md @@ -205,7 +205,7 @@ reveal_type(D.__class__) # revealed: Literal[SignatureMismatch] Retrieving the metaclass of a cyclically defined class should not cause an infinite loop. -```py path=a.pyi +```pyi class A(B): ... # error: [cyclic-class-definition] class B(C): ... # error: [cyclic-class-definition] class C(A): ... # error: [cyclic-class-definition] diff --git a/crates/red_knot_python_semantic/resources/mdtest/mro.md b/crates/red_knot_python_semantic/resources/mdtest/mro.md index dfeb30b5eb..7803e45e56 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/mro.md +++ b/crates/red_knot_python_semantic/resources/mdtest/mro.md @@ -347,7 +347,7 @@ reveal_type(unknown_object.__mro__) # revealed: Unknown These are invalid, but we need to be able to handle them gracefully without panicking. -```py path=a.pyi +```pyi class Foo(Foo): ... # error: [cyclic-class-definition] reveal_type(Foo) # revealed: Literal[Foo] @@ -365,7 +365,7 @@ reveal_type(Boz.__mro__) # revealed: tuple[Literal[Boz], Unknown, Literal[objec These are similarly unlikely, but we still shouldn't crash: -```py path=a.pyi +```pyi class Foo(Bar): ... # error: [cyclic-class-definition] class Bar(Baz): ... # error: [cyclic-class-definition] class Baz(Foo): ... # error: [cyclic-class-definition] @@ -377,7 +377,7 @@ reveal_type(Baz.__mro__) # revealed: tuple[Literal[Baz], Unknown, Literal[objec ## Classes with cycles in their MROs, and multiple inheritance -```py path=a.pyi +```pyi class Spam: ... class Foo(Bar): ... # error: [cyclic-class-definition] class Bar(Baz): ... # error: [cyclic-class-definition] @@ -390,7 +390,7 @@ reveal_type(Baz.__mro__) # revealed: tuple[Literal[Baz], Unknown, Literal[objec ## Classes with cycles in their MRO, and a sub-graph -```py path=a.pyi +```pyi class FooCycle(BarCycle): ... # error: [cyclic-class-definition] class Foo: ... class BarCycle(FooCycle): ... # error: [cyclic-class-definition] diff --git a/crates/red_knot_python_semantic/resources/mdtest/regression/14334_diagnostics_in_wrong_file.md b/crates/red_knot_python_semantic/resources/mdtest/regression/14334_diagnostics_in_wrong_file.md index 821ba0af13..d7d50db5e6 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/regression/14334_diagnostics_in_wrong_file.md +++ b/crates/red_knot_python_semantic/resources/mdtest/regression/14334_diagnostics_in_wrong_file.md @@ -2,12 +2,16 @@ Regression test for [this issue](https://github.com/astral-sh/ruff/issues/14334). -```py path=base.py +`base.py`: + +```py # error: [invalid-base] class Base(2): ... ``` -```py path=a.py +`a.py`: + +```py # No error here from base import Base ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/scopes/moduletype_attrs.md b/crates/red_knot_python_semantic/resources/mdtest/scopes/moduletype_attrs.md index 21daddc5af..9b9339d128 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/scopes/moduletype_attrs.md +++ b/crates/red_knot_python_semantic/resources/mdtest/scopes/moduletype_attrs.md @@ -29,7 +29,9 @@ def foo(): However, three attributes on `types.ModuleType` are not present as implicit module globals; these are excluded: -```py path=unbound_dunders.py +`unbound_dunders.py`: + +```py # error: [unresolved-reference] # revealed: Unknown reveal_type(__getattr__) @@ -70,7 +72,9 @@ Typeshed includes a fake `__getattr__` method in the stub for `types.ModuleType` dynamic imports; but we ignore that for module-literal types where we know exactly which module we're dealing with: -```py path=__getattr__.py +`__getattr__.py`: + +```py import typing # error: [unresolved-attribute] @@ -83,13 +87,17 @@ It's impossible to override the `__dict__` attribute of `types.ModuleType` insta module; we should prioritise the attribute in the `types.ModuleType` stub over a variable named `__dict__` in the module's global namespace: -```py path=foo.py +`foo.py`: + +```py __dict__ = "foo" reveal_type(__dict__) # revealed: Literal["foo"] ``` -```py path=bar.py +`bar.py`: + +```py import foo from foo import __dict__ as foo_dict diff --git a/crates/red_knot_python_semantic/resources/mdtest/shadowing/function.md b/crates/red_knot_python_semantic/resources/mdtest/shadowing/function.md index 591249dde7..806d929c84 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/shadowing/function.md +++ b/crates/red_knot_python_semantic/resources/mdtest/shadowing/function.md @@ -5,14 +5,18 @@ Parameter `x` of type `str` is shadowed and reassigned with a new `int` value inside the function. No diagnostics should be generated. -```py path=a.py +`a.py`: + +```py def f(x: str): x: int = int(x) ``` ## Implicit error -```py path=a.py +`a.py`: + +```py def f(): ... f = 1 # error: "Implicit shadowing of function `f`; annotate to make it explicit if this is intentional" @@ -20,7 +24,9 @@ f = 1 # error: "Implicit shadowing of function `f`; annotate to make it explici ## Explicit shadowing -```py path=a.py +`a.py`: + +```py def f(): ... f: int = 1 diff --git a/crates/red_knot_python_semantic/resources/mdtest/statically_known_branches.md b/crates/red_knot_python_semantic/resources/mdtest/statically_known_branches.md index 5b5f7c686c..3fe3967d8b 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/statically_known_branches.md +++ b/crates/red_knot_python_semantic/resources/mdtest/statically_known_branches.md @@ -7,7 +7,9 @@ branches whose conditions we can statically determine to be always true or alway useful for `sys.version_info` branches, which can make new features available based on the Python version: -```py path=module1.py +`module1.py`: + +```py import sys if sys.version_info >= (3, 9): @@ -17,7 +19,9 @@ if sys.version_info >= (3, 9): If we can statically determine that the condition is always true, then we can also understand that `SomeFeature` is always bound, without raising any errors: -```py path=test1.py +`test1.py`: + +```py from module1 import SomeFeature # SomeFeature is unconditionally available here, because we are on Python 3.9 or newer: @@ -27,11 +31,15 @@ reveal_type(SomeFeature) # revealed: str Another scenario where this is useful is for `typing.TYPE_CHECKING` branches, which are often used for conditional imports: -```py path=module2.py +`module2.py`: + +```py class SomeType: ... ``` -```py path=test2.py +`test2.py`: + +```py import typing if typing.TYPE_CHECKING: @@ -167,7 +175,9 @@ statically known conditions, but here, we show that the results are truly based not some special handling of specific conditions in semantic index building. We use two modules to demonstrate this, since semantic index building is inherently single-module: -```py path=module.py +`module.py`: + +```py from typing import Literal class AlwaysTrue: @@ -1426,7 +1436,9 @@ def f(): #### Always false, unbound -```py path=module.py +`module.py`: + +```py if False: symbol = 1 ``` @@ -1438,7 +1450,9 @@ from module import symbol #### Always true, bound -```py path=module.py +`module.py`: + +```py if True: symbol = 1 ``` @@ -1450,7 +1464,9 @@ from module import symbol #### Ambiguous, possibly unbound -```py path=module.py +`module.py`: + +```py def flag() -> bool: return True @@ -1465,7 +1481,9 @@ from module import symbol #### Always false, undeclared -```py path=module.py +`module.py`: + +```py if False: symbol: int ``` @@ -1479,7 +1497,9 @@ reveal_type(symbol) # revealed: Unknown #### Always true, declared -```py path=module.py +`module.py`: + +```py if True: symbol: int ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/stubs/class.md b/crates/red_knot_python_semantic/resources/mdtest/stubs/class.md index d2d209b3f4..e3b418716e 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/stubs/class.md +++ b/crates/red_knot_python_semantic/resources/mdtest/stubs/class.md @@ -5,7 +5,7 @@ In type stubs, classes can reference themselves in their base class definitions. For example, in `typeshed`, we have `class str(Sequence[str]): ...`. -```py path=a.pyi +```pyi class Foo[T]: ... # TODO: actually is subscriptable diff --git a/crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md b/crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md index fd5c826b98..6b147fca57 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md +++ b/crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md @@ -5,7 +5,7 @@ The ellipsis literal `...` can be used as a placeholder default value for a function parameter, in a stub file only, regardless of the type of the parameter. -```py path=test.pyi +```pyi def f(x: int = ...) -> None: reveal_type(x) # revealed: int @@ -18,7 +18,7 @@ def f2(x: str = ...) -> None: The ellipsis literal can be assigned to a class or module symbol, regardless of its declared type, in a stub file only. -```py path=test.pyi +```pyi y: bytes = ... reveal_type(y) # revealed: bytes x = ... @@ -35,7 +35,7 @@ reveal_type(Foo.y) # revealed: int No diagnostic is emitted if an ellipsis literal is "unpacked" in a stub file as part of an assignment statement: -```py path=test.pyi +```pyi x, y = ... reveal_type(x) # revealed: Unknown reveal_type(y) # revealed: Unknown @@ -46,7 +46,7 @@ reveal_type(y) # revealed: Unknown Iterating over an ellipsis literal as part of a `for` loop in a stub is invalid, however, and results in a diagnostic: -```py path=test.pyi +```pyi # error: [not-iterable] "Object of type `ellipsis` is not iterable" for a, b in ...: reveal_type(a) # revealed: Unknown @@ -72,7 +72,7 @@ reveal_type(b) # revealed: ellipsis There is no special treatment of the builtin name `Ellipsis` in stubs, only of `...` literals. -```py path=test.pyi +```pyi # error: 7 [invalid-parameter-default] "Default value of type `ellipsis` is not assignable to annotated parameter type `int`" def f(x: int = Ellipsis) -> None: ... ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/suppressions/type_ignore.md b/crates/red_knot_python_semantic/resources/mdtest/suppressions/type_ignore.md index ee76a56b29..14d20460fc 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/suppressions/type_ignore.md +++ b/crates/red_knot_python_semantic/resources/mdtest/suppressions/type_ignore.md @@ -37,7 +37,9 @@ child expression now suppresses errors in the outer expression. For example, the `type: ignore` comment in this example suppresses the error of adding `2` to `"test"` and adding `"other"` to the result of the cast. -```py path=nested.py +`nested.py`: + +```py # fmt: off from typing import cast diff --git a/crates/red_knot_python_semantic/resources/mdtest/sys_version_info.md b/crates/red_knot_python_semantic/resources/mdtest/sys_version_info.md index 11f0eb52c8..64c4e6ccf7 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/sys_version_info.md +++ b/crates/red_knot_python_semantic/resources/mdtest/sys_version_info.md @@ -86,14 +86,20 @@ reveal_type(bar >= (3, 9)) # revealed: Literal[True] Only comparisons with the symbol `version_info` from the `sys` module produce literal types: -```py path=package/__init__.py +`package/__init__.py`: + +```py ``` -```py path=package/sys.py +`package/sys.py`: + +```py version_info: tuple[int, int] = (4, 2) ``` -```py path=package/script.py +`package/script.py`: + +```py from .sys import version_info reveal_type(version_info >= (3, 9)) # revealed: bool @@ -103,7 +109,9 @@ reveal_type(version_info >= (3, 9)) # revealed: bool The fields of `sys.version_info` can be accessed by name: -```py path=a.py +`a.py`: + +```py import sys reveal_type(sys.version_info.major >= 3) # revealed: Literal[True] @@ -114,7 +122,9 @@ reveal_type(sys.version_info.minor >= 10) # revealed: Literal[False] But the `micro`, `releaselevel` and `serial` fields are inferred as `@Todo` until we support properties on instance types: -```py path=b.py +`b.py`: + +```py import sys reveal_type(sys.version_info.micro) # revealed: @Todo(@property) diff --git a/crates/red_knot_python_semantic/resources/mdtest/type_of/basic.md b/crates/red_knot_python_semantic/resources/mdtest/type_of/basic.md index 2a7d51c62b..1e5f4bdc5b 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/type_of/basic.md +++ b/crates/red_knot_python_semantic/resources/mdtest/type_of/basic.md @@ -39,7 +39,9 @@ def f(c: type[A]): reveal_type(c) # revealed: type[A] ``` -```py path=a.py +`a.py`: + +```py class A: ... ``` @@ -52,23 +54,31 @@ def f(c: type[a.B]): reveal_type(c) # revealed: type[B] ``` -```py path=a.py +`a.py`: + +```py class B: ... ``` ## Deeply qualified class literal from another module -```py path=a/test.py +`a/test.py`: + +```py import a.b def f(c: type[a.b.C]): reveal_type(c) # revealed: type[C] ``` -```py path=a/__init__.py +`a/__init__.py`: + +```py ``` -```py path=a/b.py +`a/b.py`: + +```py class C: ... ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/unary/not.md b/crates/red_knot_python_semantic/resources/mdtest/unary/not.md index 8153ffeacf..37ddfbcc65 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/unary/not.md +++ b/crates/red_knot_python_semantic/resources/mdtest/unary/not.md @@ -28,7 +28,9 @@ reveal_type(not b) # revealed: Literal[False] reveal_type(not warnings) # revealed: Literal[False] ``` -```py path=b.py +`b.py`: + +```py y = 1 ``` diff --git a/crates/red_knot_test/README.md b/crates/red_knot_test/README.md index 9670b7c61b..5a3b27354f 100644 --- a/crates/red_knot_test/README.md +++ b/crates/red_knot_test/README.md @@ -20,10 +20,10 @@ reveal_type(1) # revealed: Literal[1] ```` When running this test, the mdtest framework will write a file with these contents to the default -file path (`/src/test.py`) in its in-memory file system, run a type check on that file, and then -match the resulting diagnostics with the assertions in the test. Assertions are in the form of -Python comments. If all diagnostics and all assertions are matched, the test passes; otherwise, it -fails. +file path (`/src/mdtest_snippet__1.py`) in its in-memory file system, run a type check on that file, +and then match the resulting diagnostics with the assertions in the test. Assertions are in the form +of Python comments. If all diagnostics and all assertions are matched, the test passes; otherwise, +it fails.