diff --git a/crates/red_knot_python_semantic/resources/mdtest/.mdformat.toml b/crates/red_knot_python_semantic/resources/mdtest/.mdformat.toml new file mode 100644 index 0000000000000..0163082076afe --- /dev/null +++ b/crates/red_knot_python_semantic/resources/mdtest/.mdformat.toml @@ -0,0 +1 @@ +wrap = 100 diff --git a/crates/red_knot_python_semantic/resources/mdtest/assignment/unbound.md b/crates/red_knot_python_semantic/resources/mdtest/assignment/unbound.md index 04a5b90e80752..2a27866325f32 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/assignment/unbound.md +++ b/crates/red_knot_python_semantic/resources/mdtest/assignment/unbound.md @@ -14,11 +14,10 @@ foo = 1 reveal_type(x) ``` -Note: in this particular example, one could argue that the most likely error would -be a wrong order of the `x`/`foo` definitions, and so it could be desirable to infer -`Literal[1]` for the type of `x`. On the other hand, there might be a variable `fob` -a little higher up in this file, and the actual error might have been just a typo. -Inferring `Unknown` thus seems like the safest option. +Note: in this particular example, one could argue that the most likely error would be a wrong order +of the `x`/`foo` definitions, and so it could be desirable to infer `Literal[1]` for the type of +`x`. On the other hand, there might be a variable `fob` a little higher up in this file, and the +actual error might have been just a typo. Inferring `Unknown` thus seems like the safest option. ## Unbound class variable diff --git a/crates/red_knot_python_semantic/resources/mdtest/binary/instances.md b/crates/red_knot_python_semantic/resources/mdtest/binary/instances.md index 9375f197c0be3..c2f8f2f1a844c 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/binary/instances.md +++ b/crates/red_knot_python_semantic/resources/mdtest/binary/instances.md @@ -9,8 +9,8 @@ For references, see: ## Operations -We support inference for all Python's binary operators: -`+`, `-`, `*`, `@`, `/`, `//`, `%`, `**`, `<<`, `>>`, `&`, `^`, and `|`. +We support inference for all Python's binary operators: `+`, `-`, `*`, `@`, `/`, `//`, `%`, `**`, +`<<`, `>>`, `&`, `^`, and `|`. ```py class A: @@ -152,9 +152,8 @@ reveal_type(B() - A()) # revealed: int ## Non-reflected precedence in general -In general, if the left-hand side defines `__add__` and the right-hand side -defines `__radd__` and the right-hand side is not a subtype of the left-hand -side, `lhs.__add__` will take precedence: +In general, if the left-hand side defines `__add__` and the right-hand side defines `__radd__` and +the right-hand side is not a subtype of the left-hand side, `lhs.__add__` will take precedence: ```py class A: @@ -181,9 +180,8 @@ reveal_type(C() + C()) # revealed: int ## Reflected precedence for subtypes (in some cases) -If the right-hand operand is a subtype of the left-hand operand and has a -different implementation of the reflected method, the reflected method on the -right-hand operand takes precedence. +If the right-hand operand is a subtype of the left-hand operand and has a different implementation +of the reflected method, the reflected method on the right-hand operand takes precedence. ```py class A: @@ -213,9 +211,8 @@ reveal_type(A() + C()) # revealed: str ## Reflected precedence 2 -If the right-hand operand is a subtype of the left-hand operand, but does not -override the reflected method, the left-hand operand's non-reflected method -still takes precedence: +If the right-hand operand is a subtype of the left-hand operand, but does not override the reflected +method, the left-hand operand's non-reflected method still takes precedence: ```py class A: @@ -232,17 +229,15 @@ reveal_type(A() + B()) # revealed: str ## Only reflected supported -For example, at runtime, `(1).__add__(1.2)` is `NotImplemented`, but -`(1.2).__radd__(1) == 2.2`, meaning that `1 + 1.2` succeeds at runtime -(producing `2.2`). The runtime tries the second one only if the first one -returns `NotImplemented` to signal failure. +For example, at runtime, `(1).__add__(1.2)` is `NotImplemented`, but `(1.2).__radd__(1) == 2.2`, +meaning that `1 + 1.2` succeeds at runtime (producing `2.2`). The runtime tries the second one only +if the first one returns `NotImplemented` to signal failure. -Typeshed and other stubs annotate dunder-method calls that would return -`NotImplemented` as being "illegal" calls. `int.__add__` is annotated as only -"accepting" `int`s, even though it strictly-speaking "accepts" any other object -without raising an exception -- it will simply return `NotImplemented`, -allowing the runtime to try the `__radd__` method of the right-hand operand -as well. +Typeshed and other stubs annotate dunder-method calls that would return `NotImplemented` as being +"illegal" calls. `int.__add__` is annotated as only "accepting" `int`s, even though it +strictly-speaking "accepts" any other object without raising an exception -- it will simply return +`NotImplemented`, allowing the runtime to try the `__radd__` method of the right-hand operand as +well. ```py class A: @@ -308,8 +303,8 @@ reveal_type(y + 4.12) # revealed: int ## With literal types -When we have a literal type for one operand, we're able to fall back to the -instance handling for its instance super-type. +When we have a literal type for one operand, we're able to fall back to the instance handling for +its instance super-type. ```py class A: @@ -348,15 +343,13 @@ reveal_type(literal_string_instance + A()) # revealed: @Todo ## Operations involving instances of classes inheriting from `Any` -`Any` and `Unknown` represent a set of possible runtime objects, wherein the -bounds of the set are unknown. Whether the left-hand operand's dunder or the -right-hand operand's reflected dunder depends on whether the right-hand operand -is an instance of a class that is a subclass of the left-hand operand's class -and overrides the reflected dunder. In the following example, because of the -unknowable nature of `Any`/`Unknown`, we must consider both possibilities: -`Any`/`Unknown` might resolve to an unknown third class that inherits from `X` -and overrides `__radd__`; but it also might not. Thus, the correct answer here -for the `reveal_type` is `int | Unknown`. +`Any` and `Unknown` represent a set of possible runtime objects, wherein the bounds of the set are +unknown. Whether the left-hand operand's dunder or the right-hand operand's reflected dunder depends +on whether the right-hand operand is an instance of a class that is a subclass of the left-hand +operand's class and overrides the reflected dunder. In the following example, because of the +unknowable nature of `Any`/`Unknown`, we must consider both possibilities: `Any`/`Unknown` might +resolve to an unknown third class that inherits from `X` and overrides `__radd__`; but it also might +not. Thus, the correct answer here for the `reveal_type` is `int | Unknown`. ```py from does_not_exist import Foo # error: [unresolved-import] @@ -426,10 +419,9 @@ reveal_type(B() + C()) ### Reflected dunder is not tried between two objects of the same type -For the specific case where the left-hand operand is the exact same type as the -right-hand operand, the reflected dunder of the right-hand operand is not -tried; the runtime short-circuits after trying the unreflected dunder of the -left-hand operand. For context, see +For the specific case where the left-hand operand is the exact same type as the right-hand operand, +the reflected dunder of the right-hand operand is not tried; the runtime short-circuits after trying +the unreflected dunder of the left-hand operand. For context, see [this mailing list discussion](https://mail.python.org/archives/list/python-dev@python.org/thread/7NZUCODEAPQFMRFXYRMGJXDSIS3WJYIV/). ```py diff --git a/crates/red_knot_python_semantic/resources/mdtest/comparison/byte_literals.md b/crates/red_knot_python_semantic/resources/mdtest/comparison/byte_literals.md index 27aee9f1c8005..2a76e5b227504 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/byte_literals.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/byte_literals.md @@ -1,7 +1,7 @@ # Comparison: Byte literals -These tests assert that we infer precise `Literal` types for comparisons between objects -inferred as having `Literal` bytes types: +These tests assert that we infer precise `Literal` types for comparisons between objects inferred as +having `Literal` bytes types: ```py reveal_type(b"abc" == b"abc") # revealed: Literal[True] diff --git a/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/membership_test.md b/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/membership_test.md index 21595db3f5626..a8dcba2dcff42 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/membership_test.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/membership_test.md @@ -1,8 +1,8 @@ # Comparison: Membership Test -In Python, the term "membership test operators" refers to the operators -`in` and `not in`. To customize their behavior, classes can implement one of -the special methods `__contains__`, `__iter__`, or `__getitem__`. +In Python, the term "membership test operators" refers to the operators `in` and `not in`. To +customize their behavior, classes can implement one of the special methods `__contains__`, +`__iter__`, or `__getitem__`. For references, see: @@ -28,8 +28,8 @@ reveal_type(42 not in A()) # revealed: bool ## Implements `__iter__` -Classes that don't implement `__contains__`, but do implement `__iter__`, also -support containment checks; the needle will be sought in their iterated items: +Classes that don't implement `__contains__`, but do implement `__iter__`, also support containment +checks; the needle will be sought in their iterated items: ```py class StringIterator: @@ -48,11 +48,10 @@ reveal_type(42 not in A()) # revealed: bool ## Implements `__getitems__` -The final fallback is to implement `__getitem__` for integer keys. Python will -call `__getitem__` with `0`, `1`, `2`... until either the needle is found -(leading the membership test to evaluate to `True`) or `__getitem__` raises -`IndexError` (the raised exception is swallowed, but results in the membership -test evaluating to `False`). +The final fallback is to implement `__getitem__` for integer keys. Python will call `__getitem__` +with `0`, `1`, `2`... until either the needle is found (leading the membership test to evaluate to +`True`) or `__getitem__` raises `IndexError` (the raised exception is swallowed, but results in the +membership test evaluating to `False`). ```py class A: @@ -67,8 +66,8 @@ reveal_type(42 not in A()) # revealed: bool ## Wrong Return Type -Python coerces the results of containment checks to `bool`, even if `__contains__` -returns a non-bool: +Python coerces the results of containment checks to `bool`, even if `__contains__` returns a +non-bool: ```py class A: @@ -81,8 +80,7 @@ reveal_type("hello" not in A()) # revealed: bool ## Literal Result for `in` and `not in` -`__contains__` with a literal return type may result in a `BooleanLiteral` -outcome. +`__contains__` with a literal return type may result in a `BooleanLiteral` outcome. ```py from typing import Literal @@ -106,9 +104,8 @@ reveal_type(42 not in AlwaysFalse()) # revealed: @Todo ## No Fallback for `__contains__` -If `__contains__` is implemented, checking membership of a type it doesn't -accept is an error; it doesn't result in a fallback to `__iter__` or -`__getitem__`: +If `__contains__` is implemented, checking membership of a type it doesn't accept is an error; it +doesn't result in a fallback to `__iter__` or `__getitem__`: ```py class CheckContains: ... @@ -151,8 +148,8 @@ reveal_type(CheckGetItem() in B()) # revealed: bool ## Invalid Old-Style Iteration -If `__getitem__` is implemented but does not accept integer arguments, then -the membership test is not supported and should trigger a diagnostic. +If `__getitem__` is implemented but does not accept integer arguments, then the membership test is +not supported and should trigger a diagnostic. ```py class A: diff --git a/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/rich_comparison.md b/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/rich_comparison.md index 5e21beade127a..cffec0bf724a0 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/rich_comparison.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/instances/rich_comparison.md @@ -1,8 +1,7 @@ # Comparison: Rich Comparison -Rich comparison operations (`==`, `!=`, `<`, `<=`, `>`, `>=`) in Python are -implemented through double-underscore methods that allow customization of -comparison behavior. +Rich comparison operations (`==`, `!=`, `<`, `<=`, `>`, `>=`) in Python are implemented through +double-underscore methods that allow customization of comparison behavior. For references, see: @@ -11,9 +10,8 @@ For references, see: ## Rich Comparison Dunder Implementations For Same Class -Classes can support rich comparison by implementing dunder methods like -`__eq__`, `__ne__`, etc. The most common case involves implementing these -methods for the same type: +Classes can support rich comparison by implementing dunder methods like `__eq__`, `__ne__`, etc. The +most common case involves implementing these methods for the same type: ```py from __future__ import annotations @@ -47,8 +45,8 @@ reveal_type(A() >= A()) # revealed: set ## Rich Comparison Dunder Implementations for Other Class -In some cases, classes may implement rich comparison dunder methods for -comparisons with a different type: +In some cases, classes may implement rich comparison dunder methods for comparisons with a different +type: ```py from __future__ import annotations @@ -84,10 +82,9 @@ reveal_type(A() >= B()) # revealed: set ## Reflected Comparisons -Fallback to the right-hand side’s comparison methods occurs when the left-hand -side does not define them. Note: class `B` has its own `__eq__` and `__ne__` -methods to override those of `object`, but these methods will be ignored here -because they require a mismatched operand type. +Fallback to the right-hand side’s comparison methods occurs when the left-hand side does not define +them. Note: class `B` has its own `__eq__` and `__ne__` methods to override those of `object`, but +these methods will be ignored here because they require a mismatched operand type. ```py from __future__ import annotations @@ -148,9 +145,9 @@ reveal_type(C() <= C()) # revealed: float ## Reflected Comparisons with Subclasses -When subclasses override comparison methods, these overridden methods take -precedence over those in the parent class. Class `B` inherits from `A` and -redefines comparison methods to return types other than `A`. +When subclasses override comparison methods, these overridden methods take precedence over those in +the parent class. Class `B` inherits from `A` and redefines comparison methods to return types other +than `A`. ```py from __future__ import annotations @@ -205,9 +202,8 @@ reveal_type(A() >= B()) # revealed: bytes ## Reflected Comparisons with Subclass But Falls Back to LHS -In the case of a subclass, the right-hand side has priority. However, if the -overridden dunder method has an mismatched type to operand, the comparison will -fall back to the left-hand side. +In the case of a subclass, the right-hand side has priority. However, if the overridden dunder +method has an mismatched type to operand, the comparison will fall back to the left-hand side. ```py from __future__ import annotations @@ -233,15 +229,13 @@ reveal_type(A() > B()) # revealed: B ## Operations involving instances of classes inheriting from `Any` -`Any` and `Unknown` represent a set of possible runtime objects, wherein the -bounds of the set are unknown. Whether the left-hand operand's dunder or the -right-hand operand's reflected dunder depends on whether the right-hand operand -is an instance of a class that is a subclass of the left-hand operand's class -and overrides the reflected dunder. In the following example, because of the -unknowable nature of `Any`/`Unknown`, we must consider both possibilities: -`Any`/`Unknown` might resolve to an unknown third class that inherits from `X` -and overrides `__gt__`; but it also might not. Thus, the correct answer here for -the `reveal_type` is `int | Unknown`. +`Any` and `Unknown` represent a set of possible runtime objects, wherein the bounds of the set are +unknown. Whether the left-hand operand's dunder or the right-hand operand's reflected dunder depends +on whether the right-hand operand is an instance of a class that is a subclass of the left-hand +operand's class and overrides the reflected dunder. In the following example, because of the +unknowable nature of `Any`/`Unknown`, we must consider both possibilities: `Any`/`Unknown` might +resolve to an unknown third class that inherits from `X` and overrides `__gt__`; but it also might +not. Thus, the correct answer here for the `reveal_type` is `int | Unknown`. (This test is referenced from `mdtest/binary/instances.md`) @@ -262,12 +256,10 @@ reveal_type(X() < Y()) # revealed: int ## Equality and Inequality Fallback -This test confirms that `==` and `!=` comparisons default to identity -comparisons (`is`, `is not`) when argument types do not match the method -signature. +This test confirms that `==` and `!=` comparisons default to identity comparisons (`is`, `is not`) +when argument types do not match the method signature. -Please refer to the -[docs](https://docs.python.org/3/reference/datamodel.html#object.__eq__) +Please refer to the [docs](https://docs.python.org/3/reference/datamodel.html#object.__eq__) ```py from __future__ import annotations diff --git a/crates/red_knot_python_semantic/resources/mdtest/comparison/non_boolean_returns.md b/crates/red_knot_python_semantic/resources/mdtest/comparison/non_boolean_returns.md index e7178fa3efa8d..bc535a5acf038 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/non_boolean_returns.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/non_boolean_returns.md @@ -5,9 +5,9 @@ Walking through examples: - `a = A() < B() < C()` 1. `A() < B() and B() < C()` - split in N comparison - 1. `A()` and `B()` - evaluate outcome types - 1. `bool` and `bool` - evaluate truthiness - 1. `A | B` - union of "first true" types + 1. `A()` and `B()` - evaluate outcome types + 1. `bool` and `bool` - evaluate truthiness + 1. `A | B` - union of "first true" types - `b = 0 < 1 < A() < 3` 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 7d3d789e07ed9..00967654f45a5 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.md @@ -82,8 +82,8 @@ reveal_type(a >= b) # revealed: bool #### Comparison Unsupported -If two tuples contain types that do not support comparison, the result may be `Unknown`. -However, `==` and `!=` are exceptions and can still provide definite results. +If two tuples contain types that do not support comparison, the result may be `Unknown`. However, +`==` and `!=` are exceptions and can still provide definite results. ```py a = (1, 2) @@ -102,8 +102,8 @@ reveal_type(a > b) # revealed: bool 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. +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 a = (1, 2) diff --git a/crates/red_knot_python_semantic/resources/mdtest/comparison/unions.md b/crates/red_knot_python_semantic/resources/mdtest/comparison/unions.md index 2a07827738c50..b6b6c738517db 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/unions.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/unions.md @@ -52,8 +52,8 @@ reveal_type(one_or_none is not None) # revealed: bool ## Union on both sides of the comparison -With unions on both sides, we need to consider the full cross product of -options when building the resulting (union) type: +With unions on both sides, we need to consider the full cross product of options when building the +resulting (union) type: ```py def bool_instance() -> bool: @@ -72,9 +72,9 @@ reveal_type(small > large) # revealed: Literal[False] ## Unsupported operations -Make sure we emit a diagnostic if *any* of the possible comparisons is -unsupported. For now, we fall back to `bool` for the result type instead of -trying to infer something more precise from the other (supported) variants: +Make sure we emit a diagnostic if *any* of the possible comparisons is unsupported. For now, we fall +back to `bool` for the result type instead of trying to infer something more precise from the other +(supported) variants: ```py def bool_instance() -> bool: 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 a4af8d4140e43..284b0f24d5620 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 @@ -1,40 +1,33 @@ # Control flow for exception handlers -These tests assert that we understand the possible "definition states" (which -symbols might or might not be defined) in the various branches of a -`try`/`except`/`else`/`finally` block. - -For a full writeup on the semantics of exception handlers, -see [this document][1]. - -The tests throughout this Markdown document use functions with names starting -with `could_raise_*` to mark definitions that might or might not succeed -(as the function could raise an exception). A type checker must assume that any -arbitrary function call could raise an exception in Python; this is just a -naming convention used in these tests for clarity, and to future-proof the -tests against possible future improvements whereby certain statements or -expressions could potentially be inferred as being incapable of causing an -exception to be raised. +These tests assert that we understand the possible "definition states" (which symbols might or might +not be defined) in the various branches of a `try`/`except`/`else`/`finally` block. + +For a full writeup on the semantics of exception handlers, see [this document][1]. + +The tests throughout this Markdown document use functions with names starting with `could_raise_*` +to mark definitions that might or might not succeed (as the function could raise an exception). A +type checker must assume that any arbitrary function call could raise an exception in Python; this +is just a naming convention used in these tests for clarity, and to future-proof the tests against +possible future improvements whereby certain statements or expressions could potentially be inferred +as being incapable of causing an exception to be raised. ## A single bare `except` -Consider the following `try`/`except` block, with a single bare `except:`. -There are different types for the variable `x` in the two branches of this -block, and we can't determine which branch might have been taken from the -perspective of code following this block. The inferred type after the block's -conclusion is therefore the union of the type at the end of the `try` suite -(`str`) and the type at the end of the `except` suite (`Literal[2]`). - -*Within* the `except` suite, we must infer a union of all possible "definition -states" we could have been in at any point during the `try` suite. This is -because control flow could have jumped to the `except` suite without any of the -`try`-suite definitions successfully completing, with only *some* of the -`try`-suite definitions successfully completing, or indeed with *all* of them -successfully completing. The type of `x` at the beginning of the `except` suite -in this example is therefore `Literal[1] | str`, taking into account that we -might have jumped to the `except` suite before the -`x = could_raise_returns_str()` redefinition, but we *also* could have jumped -to the `except` suite *after* that redefinition. +Consider the following `try`/`except` block, with a single bare `except:`. There are different types +for the variable `x` in the two branches of this block, and we can't determine which branch might +have been taken from the perspective of code following this block. The inferred type after the +block's conclusion is therefore the union of the type at the end of the `try` suite (`str`) and the +type at the end of the `except` suite (`Literal[2]`). + +*Within* the `except` suite, we must infer a union of all possible "definition states" we could have +been in at any point during the `try` suite. This is because control flow could have jumped to the +`except` suite without any of the `try`-suite definitions successfully completing, with only *some* +of the `try`-suite definitions successfully completing, or indeed with *all* of them successfully +completing. The type of `x` at the beginning of the `except` suite in this example is therefore +`Literal[1] | str`, taking into account that we might have jumped to the `except` suite before the +`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 def could_raise_returns_str() -> str: @@ -54,9 +47,8 @@ except: 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: +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 def could_raise_returns_str() -> str: @@ -74,13 +66,12 @@ reveal_type(x) # revealed: str ## A non-bare `except` -For simple `try`/`except` blocks, an `except TypeError:` handler has the same -control flow semantics as an `except:` handler. An `except TypeError:` handler -will not catch *all* exceptions: if this is the only handler, it opens up the -possibility that an exception might occur that would not be handled. However, -as described in [the document on exception-handling semantics][1], that would -lead to termination of the scope. It's therefore irrelevant to consider this -possibility when it comes to control-flow analysis. +For simple `try`/`except` blocks, an `except TypeError:` handler has the same control flow semantics +as an `except:` handler. An `except TypeError:` handler will not catch *all* exceptions: if this is +the only handler, it opens up the possibility that an exception might occur that would not be +handled. However, as described in [the document on exception-handling semantics][1], that would lead +to termination of the scope. It's therefore irrelevant to consider this possibility when it comes to +control-flow analysis. ```py def could_raise_returns_str() -> str: @@ -102,11 +93,9 @@ reveal_type(x) # revealed: str | Literal[2] ## Multiple `except` branches -If the scope reaches the final `reveal_type` call in this example, -either the `try`-block suite of statements was executed in its entirety, -or exactly one `except` suite was executed in its entirety. -The inferred type of `x` at this point is the union of the types at the end of -the three suites: +If the scope reaches the final `reveal_type` call in this example, either the `try`-block suite of +statements was executed in its entirety, or exactly one `except` suite was executed in its entirety. +The inferred type of `x` at this point is the union of the types at the end of the three suites: - At the end of `try`, `type(x) == str` - At the end of `except TypeError`, `x == 2` @@ -136,11 +125,10 @@ reveal_type(x) # revealed: str | Literal[2, 3] ## Exception handlers with `else` branches (but no `finally`) -If we reach the `reveal_type` call at the end of this scope, -either the `try` and `else` suites were both executed in their entireties, -or the `except` suite was executed in its entirety. The type of `x` at this -point is the union of the type at the end of the `else` suite and the type at -the end of the `except` suite: +If we reach the `reveal_type` call at the end of this scope, either the `try` and `else` suites were +both executed in their entireties, or the `except` suite was executed in its entirety. The type of +`x` at this point is the union of the type at the end of the `else` suite and the type at the end of +the `except` suite: - At the end of `else`, `x == 3` - At the end of `except`, `x == 2` @@ -167,10 +155,9 @@ else: reveal_type(x) # revealed: Literal[2, 3] ``` -For a block that has multiple `except` branches and an `else` branch, the same -principle applies. In order to reach the final `reveal_type` call, -either exactly one of the `except` suites must have been executed in its -entirety, or the `try` suite and the `else` suite must both have been executed +For a block that has multiple `except` branches and an `else` branch, the same principle applies. In +order to reach the final `reveal_type` call, either exactly one of the `except` suites must have +been executed in its entirety, or the `try` suite and the `else` suite must both have been executed in their entireties: ```py @@ -201,10 +188,9 @@ reveal_type(x) # revealed: Literal[2, 3, 4] ## Exception handlers with `finally` branches (but no `except` branches) -A `finally` suite is *always* executed. As such, if we reach the `reveal_type` -call at the end of 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]`: +A `finally` suite is *always* executed. As such, if we reach the `reveal_type` call at the end of +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 def could_raise_returns_str() -> str: @@ -223,15 +209,13 @@ finally: reveal_type(x) # revealed: Literal[2] ``` -If `x` was *not* redefined in the `finally` suite, however, things are somewhat -more complicated. If we reach the final `reveal_type` call, -unlike the state when we're visiting the `finally` suite, -we know that the `try`-block suite ran to completion. -This means that there are fewer possible states at this point than there were -when we were inside the `finally` block. +If `x` was *not* redefined in the `finally` suite, however, things are somewhat more complicated. If +we reach the final `reveal_type` call, unlike the state when we're visiting the `finally` suite, we +know that the `try`-block suite ran to completion. This means that there are fewer possible states +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.) +(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 def could_raise_returns_str() -> str: @@ -252,18 +236,18 @@ reveal_type(x) # revealed: str ## Combining an `except` branch with a `finally` branch -As previously stated, we do not yet have accurate inference for types *inside* -`finally` suites. When we do, however, we will have to take account of the -following possibilities inside `finally` suites: +As previously stated, we do not yet have accurate inference for types *inside* `finally` suites. +When we do, however, we will have to take account of the following possibilities inside `finally` +suites: - The `try` suite could have run to completion -- Or we could have jumped from halfway through the `try` suite to an `except` - suite, and the `except` suite ran to completion -- Or we could have jumped from halfway through the `try` suite straight to the - `finally` suite due to an unhandled exception -- Or we could have jumped from halfway through the `try` suite to an - `except` suite, only for an exception raised in the `except` suite to cause - us to jump to the `finally` suite before the `except` suite ran to completion +- Or we could have jumped from halfway through the `try` suite to an `except` suite, and the + `except` suite ran to completion +- Or we could have jumped from halfway through the `try` suite straight to the `finally` suite due + to an unhandled exception +- Or we could have jumped from halfway through the `try` suite to an `except` suite, only for an + 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 def could_raise_returns_str() -> str: @@ -296,12 +280,11 @@ finally: reveal_type(x) # revealed: Literal[2] ``` -Now for an example without a redefinition in the `finally` suite. -As before, there *should* be fewer possibilities after completion of the -`finally` suite than there were during the `finally` suite itself. -(In some control-flow possibilities, some exceptions were merely *suspended* -during the `finally` suite; these lead to the scope's termination following the -conclusion of the `finally` suite.) +Now for an example without a redefinition in the `finally` suite. As before, there *should* be fewer +possibilities after completion of the `finally` suite than there were during the `finally` suite +itself. (In some control-flow possibilities, some exceptions were merely *suspended* during the +`finally` suite; these lead to the scope's termination following the conclusion of the `finally` +suite.) ```py path=no_redef_in_finally.py def could_raise_returns_str() -> str: @@ -377,9 +360,9 @@ reveal_type(x) # revealed: str | bool | float ## Combining `except`, `else` and `finally` branches -If the exception handler has an `else` branch, we must also take into account -the possibility that control flow could have jumped to the `finally` suite from -partway through the `else` suite due to an exception raised *there*. +If the exception handler has an `else` branch, we must also take into account the possibility that +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 def could_raise_returns_str() -> str: @@ -479,15 +462,13 @@ reveal_type(x) # revealed: bool | float | slice ## Nested `try`/`except` blocks -It would take advanced analysis, which we are not yet capable of, to be able -to determine that an exception handler always suppresses all exceptions. This -is partly because it is possible for statements in `except`, `else` and -`finally` suites to raise exceptions as well as statements in `try` suites. -This means that if an exception handler is nested inside the `try` statement of -an enclosing exception handler, it should (at least for now) be treated the -same as any other node: as a suite containing statements that could possibly -raise exceptions, which would lead to control flow jumping out of that suite -prior to the suite running to completion. +It would take advanced analysis, which we are not yet capable of, to be able to determine that an +exception handler always suppresses all exceptions. This is partly because it is possible for +statements in `except`, `else` and `finally` suites to raise exceptions as well as statements in +`try` suites. This means that if an exception handler is nested inside the `try` statement of an +enclosing exception handler, it should (at least for now) be treated the same as any other node: as +a suite containing statements that could possibly raise exceptions, which would lead to control flow +jumping out of that suite prior to the suite running to completion. ```py def could_raise_returns_str() -> str: @@ -580,8 +561,8 @@ reveal_type(x) # revealed: bytearray | Bar ## Nested scopes inside `try` blocks -Shadowing a variable in an inner scope has no effect on type inference of the -variable by that name in the outer scope: +Shadowing a variable in an inner scope has no effect on type inference of the variable by that name +in the outer scope: ```py def could_raise_returns_str() -> str: diff --git a/crates/red_knot_python_semantic/resources/mdtest/generics.md b/crates/red_knot_python_semantic/resources/mdtest/generics.md index b5a2a3a723965..ff9f5ff8d165a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/generics.md +++ b/crates/red_knot_python_semantic/resources/mdtest/generics.md @@ -53,7 +53,8 @@ reveal_type(secure_box.data) # revealed: @Todo ## Cyclical class definition -In type stubs, classes can reference themselves in their base class definitions. For example, in `typeshed`, we have `class str(Sequence[str]): ...`. +In type stubs, classes can reference themselves in their base class definitions. For example, in +`typeshed`, we have `class str(Sequence[str]): ...`. This should hold true even with generics at play. diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/boolean.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/boolean.md index 3ab952839bac5..26b63f0840bd2 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/narrow/boolean.md +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/boolean.md @@ -1,10 +1,10 @@ # Narrowing in boolean expressions -In `or` expressions, the right-hand side is evaluated only if the left-hand side is **falsy**. -So when the right-hand side is evaluated, we know the left side has failed. +In `or` expressions, the right-hand side is evaluated only if the left-hand side is **falsy**. So +when the right-hand side is evaluated, we know the left side has failed. -Similarly, in `and` expressions, the right-hand side is evaluated only if the left-hand side is **truthy**. -So when the right-hand side is evaluated, we know the left side has succeeded. +Similarly, in `and` expressions, the right-hand side is evaluated only if the left-hand side is +**truthy**. So when the right-hand side is evaluated, we know the left side has succeeded. ## Narrowing in `or` diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals_is_not.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals_is_not.md index f23bae8e1e8c5..ed89f3f7ae87d 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals_is_not.md +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals_is_not.md @@ -37,9 +37,8 @@ else: ## `is not` for non-singleton types -Non-singleton types should *not* narrow the type: two instances of a -non-singleton class may occupy different addresses in memory even if -they compare equal. +Non-singleton types should *not* narrow the type: two instances of a non-singleton class may occupy +different addresses in memory even if they compare equal. ```py x = 345 diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md index 4b07d5648a760..849d5f802ce13 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md @@ -26,9 +26,8 @@ if isinstance(x, (int, object)): ## `classinfo` is a tuple of types -Note: `isinstance(x, (int, str))` should not be confused with -`isinstance(x, tuple[(int, str)])`. The former is equivalent to -`isinstance(x, int | str)`: +Note: `isinstance(x, (int, str))` should not be confused with `isinstance(x, tuple[(int, str)])`. +The former is equivalent to `isinstance(x, int | str)`: ```py def bool_instance() -> bool: 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 9255cbdfe0f87..b468ede95481a 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 @@ -2,10 +2,9 @@ ## Implicit `ModuleType` globals -All modules are instances of `types.ModuleType`. -If a name can't be found in any local or global scope, we look it up -as an attribute on `types.ModuleType` in typeshed -before deciding that the name is unbound. +All modules are instances of `types.ModuleType`. If a name can't be found in any local or global +scope, we look it up as an attribute on `types.ModuleType` in typeshed before deciding that the name +is unbound. ```py reveal_type(__name__) # revealed: str @@ -29,8 +28,8 @@ def foo(): reveal_type(__name__) # revealed: str ``` -However, three attributes on `types.ModuleType` are not present as implicit -module globals; these are excluded: +However, three attributes on `types.ModuleType` are not present as implicit module globals; these +are excluded: ```py path=unbound_dunders.py # error: [unresolved-reference] @@ -48,10 +47,10 @@ reveal_type(__init__) ## Accessed as attributes -`ModuleType` attributes can also be accessed as attributes on module-literal types. -The special attributes `__dict__` and `__init__`, and all attributes on -`builtins.object`, can also be accessed as attributes on module-literal types, -despite the fact that these are inaccessible as globals from inside the module: +`ModuleType` attributes can also be accessed as attributes on module-literal types. The special +attributes `__dict__` and `__init__`, and all attributes on `builtins.object`, can also be accessed +as attributes on module-literal types, despite the fact that these are inaccessible as globals from +inside the module: ```py import typing @@ -71,9 +70,9 @@ reveal_type(typing.__module__) # revealed: Unknown reveal_type(typing.__dict__) # revealed: @Todo ``` -Typeshed includes a fake `__getattr__` method in the stub for `types.ModuleType` -to help out with dynamic imports; but we ignore that for module-literal types -where we know exactly which module we're dealing with: +Typeshed includes a fake `__getattr__` method in the stub for `types.ModuleType` to help out with +dynamic imports; but we ignore that for module-literal types where we know exactly which module +we're dealing with: ```py path=__getattr__.py import typing @@ -83,10 +82,9 @@ reveal_type(typing.__getattr__) # revealed: Unknown ## `types.ModuleType.__dict__` takes precedence over global variable `__dict__` -It's impossible to override the `__dict__` attribute of `types.ModuleType` -instances from inside the module; we should prioritise the attribute in -the `types.ModuleType` stub over a variable named `__dict__` in the module's -global namespace: +It's impossible to override the `__dict__` attribute of `types.ModuleType` instances from inside the +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 __dict__ = "foo" @@ -106,9 +104,9 @@ reveal_type(foo_dict) # revealed: @Todo ## Conditionally global or `ModuleType` attribute -Attributes overridden in the module namespace take priority. -If a builtin name is conditionally defined as a global, however, -a name lookup should union the `ModuleType` type with the conditionally defined type: +Attributes overridden in the module namespace take priority. If a builtin name is conditionally +defined as a global, however, a name lookup should union the `ModuleType` type with the +conditionally defined type: ```py __file__ = 42 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 b3e0c81ccc84a..d7cf209f54c67 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/shadowing/function.md +++ b/crates/red_knot_python_semantic/resources/mdtest/shadowing/function.md @@ -2,7 +2,8 @@ ## Parameter -Parameter `x` of type `str` is shadowed and reassigned with a new `int` value inside the function. No diagnostics should be generated. +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 def f(x: str): 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 c679e51c0c96a..fc5ddc8b43013 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/stubs/class.md +++ b/crates/red_knot_python_semantic/resources/mdtest/stubs/class.md @@ -2,7 +2,8 @@ ## Cyclical class definition -In type stubs, classes can reference themselves in their base class definitions. For example, in `typeshed`, we have `class str(Sequence[str]): ...`. +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 class C(C): ... diff --git a/crates/red_knot_python_semantic/resources/mdtest/subscript/lists.md b/crates/red_knot_python_semantic/resources/mdtest/subscript/lists.md index b82b23f19265f..8e5fe60788223 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/subscript/lists.md +++ b/crates/red_knot_python_semantic/resources/mdtest/subscript/lists.md @@ -23,8 +23,7 @@ reveal_type(x["a"]) # revealed: @Todo ## Assignments within list assignment -In assignment, we might also have a named assignment. -This should also get type checked. +In assignment, we might also have a named assignment. This should also get type checked. ```py x = [1, 2, 3] diff --git a/crates/red_knot_python_semantic/resources/mdtest/subscript/stepsize_zero.md b/crates/red_knot_python_semantic/resources/mdtest/subscript/stepsize_zero.md index 06eace2d94fff..2d574a6aacc13 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/subscript/stepsize_zero.md +++ b/crates/red_knot_python_semantic/resources/mdtest/subscript/stepsize_zero.md @@ -1,9 +1,8 @@ # Stepsize zero in slices -We raise a `zero-stepsize-in-slice` diagnostic when trying to slice a literal -string, bytes, or tuple with a step size of zero (see tests in `string.md`, -`bytes.md` and `tuple.md`). But we don't want to raise this diagnostic when -slicing a custom type: +We raise a `zero-stepsize-in-slice` diagnostic when trying to slice a literal string, bytes, or +tuple with a step size of zero (see tests in `string.md`, `bytes.md` and `tuple.md`). But we don't +want to raise this diagnostic when slicing a custom type: ```py class MySequence: diff --git a/crates/red_knot_python_semantic/resources/mdtest/unpacking.md b/crates/red_knot_python_semantic/resources/mdtest/unpacking.md index 6142ae0422374..b5f4b6f36818a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/unpacking.md +++ b/crates/red_knot_python_semantic/resources/mdtest/unpacking.md @@ -145,9 +145,9 @@ reveal_type(f) # revealed: Unknown ### Non-iterable unpacking -TODO: Remove duplicate diagnostics. This is happening because for a sequence-like -assignment target, multiple definitions are created and the inference engine runs -on each of them which results in duplicate diagnostics. +TODO: Remove duplicate diagnostics. This is happening because for a sequence-like assignment target, +multiple definitions are created and the inference engine runs on each of them which results in +duplicate diagnostics. ```py # error: "Object of type `Literal[1]` is not iterable" diff --git a/crates/red_knot_python_semantic/resources/mdtest/with/async_with.md b/crates/red_knot_python_semantic/resources/mdtest/with/async_with.md index ed52a78cb75b1..72aa4cad13e15 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/with/async_with.md +++ b/crates/red_knot_python_semantic/resources/mdtest/with/async_with.md @@ -2,8 +2,9 @@ ## Basic `async with` statement -The type of the target variable in a `with` statement should be the return type from the context manager's `__aenter__` method. -However, `async with` statements aren't supported yet. This test asserts that it doesn't emit any context manager-related errors. +The type of the target variable in a `with` statement should be the return type from the context +manager's `__aenter__` method. However, `async with` statements aren't supported yet. This test +asserts that it doesn't emit any context manager-related errors. ```py class Target: ... diff --git a/crates/red_knot_python_semantic/resources/mdtest/with/with.md b/crates/red_knot_python_semantic/resources/mdtest/with/with.md index 6b29d7735d47d..ab7745223b980 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/with/with.md +++ b/crates/red_knot_python_semantic/resources/mdtest/with/with.md @@ -2,7 +2,8 @@ ## Basic `with` statement -The type of the target variable in a `with` statement is the return type from the context manager's `__enter__` method. +The type of the target variable in a `with` statement is the return type from the context manager's +`__enter__` method. ```py class Target: ...