Compare commits

..

1 Commits

Author SHA1 Message Date
Jack O'Connor
a38a18e2d3 WIP: Jack + Codex mucking around with loop control flow
(The `unsafe` code here is obviously not going to survive review.)
2026-01-12 19:22:02 -08:00
286 changed files with 1249 additions and 11154 deletions

View File

@@ -121,7 +121,7 @@ repos:
hooks:
- id: blacken-docs
language: python # means renovate will also update `additional_dependencies`
args: ["--line-length", "130"]
args: ["--pyi", "--line-length", "130"]
files: '^crates/.*/resources/mdtest/.*\.md'
exclude: |
(?x)^(

View File

@@ -106,16 +106,6 @@ impl Violation for PytestCompositeAssertion {
/// assert exc_info.value.args
/// ```
///
/// Or, for pytest 8.4.0 and later:
/// ```python
/// import pytest
///
///
/// def test_foo():
/// with pytest.raises(ZeroDivisionError, check=lambda e: e.args):
/// 1 / 0
/// ```
///
/// ## References
/// - [`pytest` documentation: `pytest.raises`](https://docs.pytest.org/en/latest/reference/reference.html#pytest-raises)
#[derive(ViolationMetadata)]

View File

@@ -101,7 +101,7 @@ pub(crate) fn private_member_access(checker: &Checker, expr: &Expr) {
}
}
// Allow some public functions whose names start with an underscore, like `os._exit()`.
// Allow some documented private methods, like `os._exit()`.
if let Some(qualified_name) = semantic.resolve_qualified_name(expr) {
if matches!(qualified_name.segments(), ["os", "_exit"]) {
return;

View File

@@ -140,7 +140,7 @@ pub(crate) fn add_required_imports(
source_type: PySourceType,
context: &LintContext,
) {
for required_import in settings.isort.required_imports.iter().rev() {
for required_import in &settings.isort.required_imports {
add_required_import(
required_import,
parsed,

View File

@@ -1,14 +1,6 @@
---
source: crates/ruff_linter/src/rules/isort/mod.rs
---
I002 [*] Missing required import: `from __future__ import generator_stop`
--> docstring.py:1:1
help: Insert required import: `from __future__ import generator_stop`
1 | """Hello, world!"""
2 + from __future__ import generator_stop
3 |
4 | x = 1
I002 [*] Missing required import: `from __future__ import annotations`
--> docstring.py:1:1
help: Insert required import: `from __future__ import annotations`
@@ -16,3 +8,11 @@ help: Insert required import: `from __future__ import annotations`
2 + from __future__ import annotations
3 |
4 | x = 1
I002 [*] Missing required import: `from __future__ import generator_stop`
--> docstring.py:1:1
help: Insert required import: `from __future__ import generator_stop`
1 | """Hello, world!"""
2 + from __future__ import generator_stop
3 |
4 | x = 1

View File

@@ -1,15 +1,6 @@
---
source: crates/ruff_linter/src/rules/isort/mod.rs
---
I002 [*] Missing required import: `from __future__ import generator_stop`
--> multiple_strings.py:1:1
help: Insert required import: `from __future__ import generator_stop`
1 | """This is a docstring."""
2 + from __future__ import generator_stop
3 | "This is not a docstring."
4 | "This is also not a docstring."
5 |
I002 [*] Missing required import: `from __future__ import annotations`
--> multiple_strings.py:1:1
help: Insert required import: `from __future__ import annotations`
@@ -17,4 +8,13 @@ help: Insert required import: `from __future__ import annotations`
2 + from __future__ import annotations
3 | "This is not a docstring."
4 | "This is also not a docstring."
5 |
I002 [*] Missing required import: `from __future__ import generator_stop`
--> multiple_strings.py:1:1
help: Insert required import: `from __future__ import generator_stop`
1 | """This is a docstring."""
2 + from __future__ import generator_stop
3 | "This is not a docstring."
4 | "This is also not a docstring."
5 |

View File

@@ -398,17 +398,17 @@ mod tests {
1 + from pipes import Template
2 + from shlex import quote
I002 [*] Missing required import: `from collections import Sequence`
--> <filename>:1:1
help: Insert required import: `from collections import Sequence`
1 + from collections import Sequence
2 | from pipes import quote, Template
I002 [*] Missing required import: `from __future__ import generator_stop`
--> <filename>:1:1
help: Insert required import: `from __future__ import generator_stop`
1 + from __future__ import generator_stop
2 | from pipes import quote, Template
I002 [*] Missing required import: `from collections import Sequence`
--> <filename>:1:1
help: Insert required import: `from collections import Sequence`
1 + from collections import Sequence
2 | from pipes import quote, Template
");
}

View File

@@ -12,7 +12,7 @@ use crate::suppression::{InvalidSuppressionKind, ParseErrorKind};
///
/// ## Example
/// ```python
/// # ruff: disable # missing codes
/// ruff: disable # missing codes
/// ```
///
/// Use instead:

207
crates/ty/docs/rules.md generated
View File

@@ -8,7 +8,7 @@
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20ambiguous-protocol-member" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L542" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L541" target="_blank">View source</a>
</small>
@@ -80,7 +80,7 @@ def test(): -> "int":
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L141" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L140" target="_blank">View source</a>
</small>
@@ -104,7 +104,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.7">0.0.7</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-top-callable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L159" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L158" target="_blank">View source</a>
</small>
@@ -135,7 +135,7 @@ def f(x: object):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L210" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L209" target="_blank">View source</a>
</small>
@@ -167,7 +167,7 @@ f(int) # error
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L236" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L235" target="_blank">View source</a>
</small>
@@ -198,7 +198,7 @@ a = 1
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L261" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L260" target="_blank">View source</a>
</small>
@@ -230,7 +230,7 @@ class C(A, B): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L287" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L286" target="_blank">View source</a>
</small>
@@ -262,7 +262,7 @@ class B(A): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-type-alias-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L313" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L312" target="_blank">View source</a>
</small>
@@ -290,7 +290,7 @@ type B = A
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.16">0.0.1-alpha.16</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L357" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L356" target="_blank">View source</a>
</small>
@@ -317,7 +317,7 @@ old_func() # emits [deprecated] diagnostic
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L335" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L334" target="_blank">View source</a>
</small>
@@ -346,7 +346,7 @@ false positives it can produce.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L378" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L377" target="_blank">View source</a>
</small>
@@ -373,7 +373,7 @@ class B(A, A): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L399" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L398" target="_blank">View source</a>
</small>
@@ -529,7 +529,7 @@ def test(): -> "Literal[5]":
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L625" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L624" target="_blank">View source</a>
</small>
@@ -559,7 +559,7 @@ class C(A, B): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L649" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L648" target="_blank">View source</a>
</small>
@@ -585,7 +585,7 @@ t[3] # IndexError: tuple index out of range
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L431" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L430" target="_blank">View source</a>
</small>
@@ -674,7 +674,7 @@ an atypical memory layout.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L703" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L702" target="_blank">View source</a>
</small>
@@ -701,7 +701,7 @@ func("foo") # error: [invalid-argument-type]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L743" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L742" target="_blank">View source</a>
</small>
@@ -729,7 +729,7 @@ a: int = ''
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2122" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2079" target="_blank">View source</a>
</small>
@@ -763,7 +763,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-await" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L765" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L764" target="_blank">View source</a>
</small>
@@ -799,7 +799,7 @@ asyncio.run(main())
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L795" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L794" target="_blank">View source</a>
</small>
@@ -823,7 +823,7 @@ class A(42): ... # error: [invalid-base]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L880" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L879" target="_blank">View source</a>
</small>
@@ -850,7 +850,7 @@ with 1:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L901" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L900" target="_blank">View source</a>
</small>
@@ -879,7 +879,7 @@ a: str
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L924" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L923" target="_blank">View source</a>
</small>
@@ -923,7 +923,7 @@ except ZeroDivisionError:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.28">0.0.1-alpha.28</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-explicit-override" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1792" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1749" target="_blank">View source</a>
</small>
@@ -965,7 +965,7 @@ class D(A):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.35">0.0.1-alpha.35</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-frozen-dataclass-subclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2373" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2330" target="_blank">View source</a>
</small>
@@ -1009,7 +1009,7 @@ class NonFrozenChild(FrozenBase): # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1002" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L959" target="_blank">View source</a>
</small>
@@ -1041,55 +1041,6 @@ class D(Generic[U, T]): ...
- [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction)
## `invalid-generic-enum`
<small>
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.12">0.0.12</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-enum" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L960" target="_blank">View source</a>
</small>
**What it does**
Checks for enum classes that are also generic.
**Why is this bad?**
Enum classes cannot be generic. Python does not support generic enums:
attempting to create one will either result in an immediate `TypeError`
at runtime, or will create a class that cannot be specialized in the way
that a normal generic class can.
**Examples**
```python
from enum import Enum
from typing import Generic, TypeVar
T = TypeVar("T")
# error: enum class cannot be generic (class creation fails with `TypeError`)
class E[T](Enum):
A = 1
# error: enum class cannot be generic (class creation fails with `TypeError`)
class F(Enum, Generic[T]):
A = 1
# error: enum class cannot be generic -- the class creation does not immediately fail...
class G(Generic[T], Enum):
A = 1
# ...but this raises `KeyError`:
x: G[int]
```
**References**
- [Python documentation: Enum](https://docs.python.org/3/library/enum.html)
## `invalid-ignore-comment`
<small>
@@ -1126,7 +1077,7 @@ a = 20 / 0 # type: ignore
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.17">0.0.1-alpha.17</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-key" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L670" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L669" target="_blank">View source</a>
</small>
@@ -1165,7 +1116,7 @@ carol = Person(name="Carol", age=25) # typo!
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1033" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L990" target="_blank">View source</a>
</small>
@@ -1200,7 +1151,7 @@ def f(t: TypeVar("U")): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1130" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1087" target="_blank">View source</a>
</small>
@@ -1234,7 +1185,7 @@ class B(metaclass=f): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-method-override" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2275" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2232" target="_blank">View source</a>
</small>
@@ -1341,7 +1292,7 @@ Correct use of `@override` is enforced by ty's `invalid-explicit-override` rule.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-named-tuple" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L577" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L576" target="_blank">View source</a>
</small>
@@ -1395,7 +1346,7 @@ AttributeError: Cannot overwrite NamedTuple attribute _asdict
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-newtype" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1106" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1063" target="_blank">View source</a>
</small>
@@ -1425,7 +1376,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1157" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1114" target="_blank">View source</a>
</small>
@@ -1475,7 +1426,7 @@ def foo(x: int) -> int: ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1256" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1213" target="_blank">View source</a>
</small>
@@ -1501,7 +1452,7 @@ def f(a: int = ''): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-paramspec" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1061" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1018" target="_blank">View source</a>
</small>
@@ -1532,7 +1483,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L513" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L512" target="_blank">View source</a>
</small>
@@ -1566,7 +1517,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1276" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1233" target="_blank">View source</a>
</small>
@@ -1615,7 +1566,7 @@ def g():
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L724" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L723" target="_blank">View source</a>
</small>
@@ -1640,7 +1591,7 @@ def func() -> int:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1319" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1276" target="_blank">View source</a>
</small>
@@ -1736,7 +1687,7 @@ class C: ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.10">0.0.10</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-total-ordering" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2411" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2368" target="_blank">View source</a>
</small>
@@ -1782,7 +1733,7 @@ class MyClass:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.6">0.0.1-alpha.6</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1085" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1042" target="_blank">View source</a>
</small>
@@ -1809,7 +1760,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1551" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1508" target="_blank">View source</a>
</small>
@@ -1856,7 +1807,7 @@ Bar[int] # error: too few arguments
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1358" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1315" target="_blank">View source</a>
</small>
@@ -1886,7 +1837,7 @@ TYPE_CHECKING = ''
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1382" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1339" target="_blank">View source</a>
</small>
@@ -1916,7 +1867,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1434" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1391" target="_blank">View source</a>
</small>
@@ -1950,7 +1901,7 @@ f(10) # Error
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1406" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1363" target="_blank">View source</a>
</small>
@@ -1984,7 +1935,7 @@ class C:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1462" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1419" target="_blank">View source</a>
</small>
@@ -2019,7 +1970,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.9">0.0.9</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-typed-dict-statement" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2250" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2207" target="_blank">View source</a>
</small>
@@ -2050,7 +2001,7 @@ class Foo(TypedDict):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1491" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1448" target="_blank">View source</a>
</small>
@@ -2075,7 +2026,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-typed-dict-key" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2223" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2180" target="_blank">View source</a>
</small>
@@ -2108,7 +2059,7 @@ alice["age"] # KeyError
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1510" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1467" target="_blank">View source</a>
</small>
@@ -2137,7 +2088,7 @@ func("string") # error: [no-matching-overload]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1592" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1549" target="_blank">View source</a>
</small>
@@ -2163,7 +2114,7 @@ for i in 34: # TypeError: 'int' object is not iterable
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-subscriptable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1533" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1490" target="_blank">View source</a>
</small>
@@ -2187,7 +2138,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20override-of-final-method" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1765" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1722" target="_blank">View source</a>
</small>
@@ -2220,7 +2171,7 @@ class B(A):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1643" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1600" target="_blank">View source</a>
</small>
@@ -2247,7 +2198,7 @@ f(1, x=2) # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20positional-only-parameter-as-kwarg" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1976" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1933" target="_blank">View source</a>
</small>
@@ -2274,7 +2225,7 @@ f(x=1) # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-attribute" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1664" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1621" target="_blank">View source</a>
</small>
@@ -2302,7 +2253,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-implicit-call" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L184" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L183" target="_blank">View source</a>
</small>
@@ -2334,7 +2285,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-import" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1686" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1643" target="_blank">View source</a>
</small>
@@ -2371,7 +2322,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1716" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1673" target="_blank">View source</a>
</small>
@@ -2435,7 +2386,7 @@ def test(): -> "int":
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2150" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2107" target="_blank">View source</a>
</small>
@@ -2462,7 +2413,7 @@ cast(int, f()) # Redundant
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2098" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2055" target="_blank">View source</a>
</small>
@@ -2492,7 +2443,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1742" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1699" target="_blank">View source</a>
</small>
@@ -2521,7 +2472,7 @@ class B(A): ... # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.30">0.0.1-alpha.30</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20super-call-in-named-tuple-method" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1910" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1867" target="_blank">View source</a>
</small>
@@ -2555,7 +2506,7 @@ class F(NamedTuple):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1850" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1807" target="_blank">View source</a>
</small>
@@ -2582,7 +2533,7 @@ f("foo") # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1828" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1785" target="_blank">View source</a>
</small>
@@ -2610,7 +2561,7 @@ def _(x: int):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1871" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1828" target="_blank">View source</a>
</small>
@@ -2656,7 +2607,7 @@ class A:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1937" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1894" target="_blank">View source</a>
</small>
@@ -2680,7 +2631,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1955" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1912" target="_blank">View source</a>
</small>
@@ -2707,7 +2658,7 @@ f(x=1, y=2) # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1997" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1954" target="_blank">View source</a>
</small>
@@ -2735,7 +2686,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.15">0.0.1-alpha.15</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2171" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2128" target="_blank">View source</a>
</small>
@@ -2793,7 +2744,7 @@ def g():
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2019" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1976" target="_blank">View source</a>
</small>
@@ -2818,7 +2769,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2038" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1995" target="_blank">View source</a>
</small>
@@ -2843,7 +2794,7 @@ print(x) # NameError: name 'x' is not defined
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.7">0.0.1-alpha.7</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L813" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L812" target="_blank">View source</a>
</small>
@@ -2882,7 +2833,7 @@ class D(C): ... # error: [unsupported-base]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1612" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1569" target="_blank">View source</a>
</small>
@@ -2919,7 +2870,7 @@ b1 < b2 < b1 # exception raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-dynamic-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L846" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L845" target="_blank">View source</a>
</small>
@@ -2960,7 +2911,7 @@ def factory(base: type[Base]) -> type:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2057" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2014" target="_blank">View source</a>
</small>
@@ -3024,7 +2975,7 @@ to `false` to prevent this rule from reporting unused `type: ignore` comments.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20useless-overload-body" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1200" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1157" target="_blank">View source</a>
</small>
@@ -3087,7 +3038,7 @@ def foo(x: int | str) -> int | str:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2079" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2036" target="_blank">View source</a>
</small>

View File

@@ -3586,7 +3586,7 @@ quux.<CURSOR>
__init_subclass__ :: bound method type[Quux].__init_subclass__() -> None
__module__ :: str
__ne__ :: bound method Quux.__ne__(value: object, /) -> bool
__new__ :: def __new__[Self](cls) -> Self
__new__ :: def __new__(cls) -> Self@__new__
__reduce__ :: bound method Quux.__reduce__() -> str | tuple[Any, ...]
__reduce_ex__ :: bound method Quux.__reduce_ex__(protocol: SupportsIndex, /) -> str | tuple[Any, ...]
__repr__ :: bound method Quux.__repr__() -> str
@@ -3667,19 +3667,19 @@ C.<CURSOR>
__mro__ :: tuple[type, ...]
__name__ :: str
__ne__ :: def __ne__(self, value: object, /) -> bool
__new__ :: def __new__[Self](cls) -> Self
__or__ :: bound method <class 'C'>.__or__[Self](value: Any, /) -> UnionType | Self
__new__ :: def __new__(cls) -> Self@__new__
__or__ :: bound method <class 'C'>.__or__[Self](value: Any, /) -> UnionType | Self@__or__
__prepare__ :: bound method <class 'Meta'>.__prepare__(name: str, bases: tuple[type, ...], /, **kwds: Any) -> MutableMapping[str, object]
__qualname__ :: str
__reduce__ :: def __reduce__(self) -> str | tuple[Any, ...]
__reduce_ex__ :: def __reduce_ex__(self, protocol: SupportsIndex, /) -> str | tuple[Any, ...]
__repr__ :: def __repr__(self) -> str
__ror__ :: bound method <class 'C'>.__ror__[Self](value: Any, /) -> UnionType | Self
__ror__ :: bound method <class 'C'>.__ror__[Self](value: Any, /) -> UnionType | Self@__ror__
__setattr__ :: def __setattr__(self, name: str, value: Any, /) -> None
__sizeof__ :: def __sizeof__(self) -> int
__str__ :: def __str__(self) -> str
__subclasscheck__ :: bound method <class 'C'>.__subclasscheck__(subclass: type, /) -> bool
__subclasses__ :: bound method <class 'C'>.__subclasses__[Self]() -> list[Self]
__subclasses__ :: bound method <class 'C'>.__subclasses__[Self]() -> list[Self@__subclasses__]
__subclasshook__ :: bound method <class 'C'>.__subclasshook__(subclass: type, /) -> bool
__text_signature__ :: str | None
__type_params__ :: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]
@@ -3737,18 +3737,18 @@ Meta.<CURSOR>
__mro__ :: tuple[type, ...]
__name__ :: str
__ne__ :: def __ne__(self, value: object, /) -> bool
__or__ :: def __or__[Self](self: Self, value: Any, /) -> UnionType | Self
__or__ :: def __or__[Self](self: Self@__or__, value: Any, /) -> UnionType | Self@__or__
__prepare__ :: bound method <class 'Meta'>.__prepare__(name: str, bases: tuple[type, ...], /, **kwds: Any) -> MutableMapping[str, object]
__qualname__ :: str
__reduce__ :: def __reduce__(self) -> str | tuple[Any, ...]
__reduce_ex__ :: def __reduce_ex__(self, protocol: SupportsIndex, /) -> str | tuple[Any, ...]
__repr__ :: def __repr__(self) -> str
__ror__ :: def __ror__[Self](self: Self, value: Any, /) -> UnionType | Self
__ror__ :: def __ror__[Self](self: Self@__ror__, value: Any, /) -> UnionType | Self@__ror__
__setattr__ :: def __setattr__(self, name: str, value: Any, /) -> None
__sizeof__ :: def __sizeof__(self) -> int
__str__ :: def __str__(self) -> str
__subclasscheck__ :: def __subclasscheck__(self, subclass: type, /) -> bool
__subclasses__ :: def __subclasses__[Self](self: Self) -> list[Self]
__subclasses__ :: def __subclasses__[Self](self: Self@__subclasses__) -> list[Self@__subclasses__]
__subclasshook__ :: bound method <class 'Meta'>.__subclasshook__(subclass: type, /) -> bool
__text_signature__ :: str | None
__type_params__ :: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]
@@ -3866,19 +3866,19 @@ Quux.<CURSOR>
__mro__ :: tuple[type, ...]
__name__ :: str
__ne__ :: def __ne__(self, value: object, /) -> bool
__new__ :: def __new__[Self](cls) -> Self
__or__ :: bound method <class 'Quux'>.__or__[Self](value: Any, /) -> UnionType | Self
__new__ :: def __new__(cls) -> Self@__new__
__or__ :: bound method <class 'Quux'>.__or__[Self](value: Any, /) -> UnionType | Self@__or__
__prepare__ :: bound method <class 'type'>.__prepare__(name: str, bases: tuple[type, ...], /, **kwds: Any) -> MutableMapping[str, object]
__qualname__ :: str
__reduce__ :: def __reduce__(self) -> str | tuple[Any, ...]
__reduce_ex__ :: def __reduce_ex__(self, protocol: SupportsIndex, /) -> str | tuple[Any, ...]
__repr__ :: def __repr__(self) -> str
__ror__ :: bound method <class 'Quux'>.__ror__[Self](value: Any, /) -> UnionType | Self
__ror__ :: bound method <class 'Quux'>.__ror__[Self](value: Any, /) -> UnionType | Self@__ror__
__setattr__ :: def __setattr__(self, name: str, value: Any, /) -> None
__sizeof__ :: def __sizeof__(self) -> int
__str__ :: def __str__(self) -> str
__subclasscheck__ :: bound method <class 'Quux'>.__subclasscheck__(subclass: type, /) -> bool
__subclasses__ :: bound method <class 'Quux'>.__subclasses__[Self]() -> list[Self]
__subclasses__ :: bound method <class 'Quux'>.__subclasses__[Self]() -> list[Self@__subclasses__]
__subclasshook__ :: bound method <class 'Quux'>.__subclasshook__(subclass: type, /) -> bool
__text_signature__ :: str | None
__type_params__ :: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]
@@ -3919,8 +3919,8 @@ Answer.<CURSOR>
__bool__ :: bound method <class 'Answer'>.__bool__() -> Literal[True]
__class__ :: <class 'EnumMeta'>
__contains__ :: bound method <class 'Answer'>.__contains__(value: object) -> bool
__copy__ :: def __copy__[Self](self) -> Self
__deepcopy__ :: def __deepcopy__[Self](self, memo: Any) -> Self
__copy__ :: def __copy__(self) -> Self@__copy__
__deepcopy__ :: def __deepcopy__(self, memo: Any) -> Self@__deepcopy__
__delattr__ :: def __delattr__(self, name: str, /) -> None
__dict__ :: dict[str, Any]
__dictoffset__ :: int
@@ -3930,34 +3930,34 @@ Answer.<CURSOR>
__flags__ :: int
__format__ :: def __format__(self, format_spec: str) -> str
__getattribute__ :: def __getattribute__(self, name: str, /) -> Any
__getitem__ :: bound method <class 'Answer'>.__getitem__[_EnumMemberT](name: str) -> _EnumMemberT
__getitem__ :: bound method <class 'Answer'>.__getitem__[_EnumMemberT](name: str) -> _EnumMemberT@__getitem__
__getstate__ :: def __getstate__(self) -> object
__hash__ :: def __hash__(self) -> int
__init__ :: def __init__(self) -> None
__init_subclass__ :: bound method <class 'Answer'>.__init_subclass__() -> None
__instancecheck__ :: bound method <class 'Answer'>.__instancecheck__(instance: Any, /) -> bool
__itemsize__ :: int
__iter__ :: bound method <class 'Answer'>.__iter__[_EnumMemberT]() -> Iterator[_EnumMemberT]
__iter__ :: bound method <class 'Answer'>.__iter__[_EnumMemberT]() -> Iterator[_EnumMemberT@__iter__]
__len__ :: bound method <class 'Answer'>.__len__() -> int
__members__ :: MappingProxyType[str, Answer]
__module__ :: str
__mro__ :: tuple[type, ...]
__name__ :: str
__ne__ :: def __ne__(self, value: object, /) -> bool
__new__ :: def __new__[Self](cls, value: object) -> Self
__or__ :: bound method <class 'Answer'>.__or__[Self](value: Any, /) -> UnionType | Self
__new__ :: def __new__(cls, value: object) -> Self@__new__
__or__ :: bound method <class 'Answer'>.__or__[Self](value: Any, /) -> UnionType | Self@__or__
__order__ :: str
__prepare__ :: bound method <class 'EnumMeta'>.__prepare__(cls: str, bases: tuple[type, ...], **kwds: Any) -> _EnumDict
__qualname__ :: str
__reduce__ :: def __reduce__(self) -> str | tuple[Any, ...]
__repr__ :: def __repr__(self) -> str
__reversed__ :: bound method <class 'Answer'>.__reversed__[_EnumMemberT]() -> Iterator[_EnumMemberT]
__ror__ :: bound method <class 'Answer'>.__ror__[Self](value: Any, /) -> UnionType | Self
__reversed__ :: bound method <class 'Answer'>.__reversed__[_EnumMemberT]() -> Iterator[_EnumMemberT@__reversed__]
__ror__ :: bound method <class 'Answer'>.__ror__[Self](value: Any, /) -> UnionType | Self@__ror__
__setattr__ :: def __setattr__(self, name: str, value: Any, /) -> None
__sizeof__ :: def __sizeof__(self) -> int
__str__ :: def __str__(self) -> str
__subclasscheck__ :: bound method <class 'Answer'>.__subclasscheck__(subclass: type, /) -> bool
__subclasses__ :: bound method <class 'Answer'>.__subclasses__[Self]() -> list[Self]
__subclasses__ :: bound method <class 'Answer'>.__subclasses__[Self]() -> list[Self@__subclasses__]
__subclasshook__ :: bound method <class 'Answer'>.__subclasshook__(subclass: type, /) -> bool
__text_signature__ :: str | None
__type_params__ :: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]
@@ -3999,7 +3999,7 @@ quux.<CURSOR>
index :: bound method Quux.index(value: Any, start: SupportsIndex = 0, stop: SupportsIndex = ..., /) -> int
x :: int
y :: str
__add__ :: Overload[(value: tuple[int | str, ...], /) -> tuple[int | str, ...], [_T](value: tuple[_T, ...], /) -> tuple[int | str | _T, ...]]
__add__ :: Overload[(value: tuple[int | str, ...], /) -> tuple[int | str, ...], (value: tuple[_T@__add__, ...], /) -> tuple[int | str | _T@__add__, ...]]
__annotations__ :: dict[str, Any]
__class__ :: type[Quux]
__class_getitem__ :: bound method type[Quux].__class_getitem__(item: Any, /) -> GenericAlias

View File

@@ -9,19 +9,15 @@
```py
from typing_extensions import Annotated
def _(x: Annotated[int, "foo"]):
reveal_type(x) # revealed: int
def _(x: Annotated[int, lambda: 0 + 1 * 2 // 3, _(4)]):
reveal_type(x) # revealed: int
def _(x: Annotated[int, "arbitrary", "metadata", "elements", "are", "fine"]):
reveal_type(x) # revealed: int
def _(x: Annotated[tuple[str, int], bytes]):
reveal_type(x) # revealed: tuple[str, int]
```
@@ -33,12 +29,10 @@ It is invalid to parameterize `Annotated` with less than two arguments.
```py
from typing_extensions import Annotated
# error: [invalid-type-form] "`typing.Annotated` requires at least two arguments when used in a type expression"
def _(x: Annotated):
reveal_type(x) # revealed: Unknown
def _(flag: bool):
if flag:
X = Annotated
@@ -49,17 +43,14 @@ def _(flag: bool):
def f(y: X):
reveal_type(y) # revealed: Unknown | bool
# error: [invalid-type-form] "`typing.Annotated` requires at least two arguments when used in a type expression"
def _(x: Annotated | bool):
reveal_type(x) # revealed: Unknown | bool
# error: [invalid-type-form] "Special form `typing.Annotated` expected at least 2 arguments (one type and at least one metadata element)"
def _(x: Annotated[()]):
reveal_type(x) # revealed: Unknown
# error: [invalid-type-form]
def _(x: Annotated[int]):
# `Annotated[T]` is invalid and will raise an error at runtime,
@@ -68,7 +59,6 @@ def _(x: Annotated[int]):
# Same for the `(int,)` form below.
reveal_type(x) # revealed: int
# error: [invalid-type-form]
def _(x: Annotated[(int,)]):
reveal_type(x) # revealed: int
@@ -84,24 +74,18 @@ Inheriting from `Annotated[T, ...]` is equivalent to inheriting from `T` itself.
from typing_extensions import Annotated
from ty_extensions import reveal_mro
class C(Annotated[int, "foo"]): ...
# revealed: (<class 'C'>, <class 'int'>, <class 'object'>)
reveal_mro(C)
class D(Annotated[list[str], "foo"]): ...
# revealed: (<class 'D'>, <class 'list[str]'>, <class 'MutableSequence[str]'>, <class 'Sequence[str]'>, <class 'Reversible[str]'>, <class 'Collection[str]'>, <class 'Iterable[str]'>, <class 'Container[str]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(D)
class E(Annotated[list["E"], "metadata"]): ...
# error: [revealed-type] "Revealed MRO: (<class 'E'>, <class 'list[E]'>, <class 'MutableSequence[E]'>, <class 'Sequence[E]'>, <class 'Reversible[E]'>, <class 'Collection[E]'>, <class 'Iterable[E]'>, <class 'Container[E]'>, typing.Protocol, typing.Generic, <class 'object'>)"
reveal_mro(E)
```
@@ -112,11 +96,9 @@ reveal_mro(E)
from typing_extensions import Annotated
from ty_extensions import reveal_mro
# At runtime, this is an error.
# error: [invalid-base]
class C(Annotated): ...
reveal_mro(C) # revealed: (<class 'C'>, Unknown, <class 'object'>)
```

View File

@@ -10,7 +10,6 @@ from typing import Any
x: Any = 1
x = "foo"
def f():
reveal_type(x) # revealed: Any
```
@@ -25,7 +24,6 @@ from typing import Any as RenamedAny
x: RenamedAny = 1
x = "foo"
def f():
reveal_type(x) # revealed: Any
```
@@ -38,14 +36,11 @@ isn't a spelling of the Any type.
```py
class Any: ...
x: Any
def f():
reveal_type(x) # revealed: Any
# This verifies that we're not accidentally seeing typing.Any, since str is assignable
# to that but not to our locally defined class.
y: Any = "not an Any" # error: [invalid-assignment]
@@ -63,10 +58,8 @@ allowed, even when the unknown superclass is `int`. The assignment to `y` should
from typing import Any
from ty_extensions import reveal_mro
class SubclassOfAny(Any): ...
reveal_mro(SubclassOfAny) # revealed: (<class 'SubclassOfAny'>, Any, <class 'object'>)
x: SubclassOfAny = 1 # error: [invalid-assignment]
@@ -79,18 +72,14 @@ possibly be a subclass of `FinalClass`:
```py
from typing import final
@final
class FinalClass: ...
f: FinalClass = SubclassOfAny() # error: [invalid-assignment]
@final
class OtherFinalClass: ...
f: FinalClass | OtherFinalClass = SubclassOfAny() # error: [invalid-assignment]
```
@@ -99,44 +88,33 @@ A subclass of `Any` can also be assigned to arbitrary `Callable` and `Protocol`
```py
from typing import Callable, Any, Protocol
def takes_callable1(f: Callable):
f()
takes_callable1(SubclassOfAny())
def takes_callable2(f: Callable[[int], None]):
f(1)
takes_callable2(SubclassOfAny())
class CallbackProtocol(Protocol):
def __call__(self, x: int, /) -> None: ...
def takes_callback_proto(f: CallbackProtocol):
f(1)
takes_callback_proto(SubclassOfAny())
class OtherProtocol(Protocol):
x: int
@property
def foo(self) -> bytes: ...
@foo.setter
def foo(self, x: str) -> None: ...
def takes_other_protocol(f: OtherProtocol): ...
takes_other_protocol(SubclassOfAny())
```
@@ -145,11 +123,9 @@ A subclass of `Any` cannot be assigned to literal types, since those cannot be s
```py
from typing import Any, Literal
class MockAny(Any):
pass
x: Literal[1] = MockAny() # error: [invalid-assignment]
```
@@ -185,7 +161,6 @@ static_assert(is_assignable_to(TypeOf[Any], type))
```py
from typing import Any
# error: [invalid-type-form] "Special form `typing.Any` expected no type parameter"
def f(x: Any[int]):
reveal_type(x) # revealed: Unknown

View File

@@ -22,7 +22,6 @@ A bare `Callable` without any type arguments:
```py
from typing import Callable
def _(c: Callable):
reveal_type(c) # revealed: (...) -> Unknown
```
@@ -34,7 +33,6 @@ When it's not a list:
```py
from typing import Callable
# error: [invalid-type-form] "The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`"
def _(c: Callable[int, str]):
reveal_type(c) # revealed: (...) -> Unknown
@@ -65,7 +63,6 @@ Using a parameter list:
```py
from typing import Callable
# error: [invalid-type-form] "Special form `typing.Callable` expected exactly two arguments (parameter types and return type)"
def _(c: Callable[[int, str]]):
reveal_type(c) # revealed: (...) -> Unknown
@@ -114,7 +111,6 @@ which argument corresponds to either the parameters or the return type.
```py
from typing import Callable
# error: [invalid-type-form] "Special form `typing.Callable` expected exactly two arguments (parameter types and return type)"
def _(c: Callable[[int], str, str]):
reveal_type(c) # revealed: (...) -> Unknown
@@ -155,7 +151,6 @@ def _(c: Callable[
```py
from typing import Callable
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression"
def _(c: Callable[[int], [str]]):
reveal_type(c) # revealed: (int, /) -> Unknown
@@ -185,7 +180,6 @@ A simple `Callable` with multiple parameters and a return type:
```py
from typing import Callable
def _(c: Callable[[int, str], int]):
reveal_type(c) # revealed: (int, str, /) -> int
```
@@ -195,7 +189,6 @@ def _(c: Callable[[int, str], int]):
```py
from typing import Callable, Union
def _(
c: Callable[[Union[int, str]], int] | None,
d: None | Callable[[Union[int, str]], int],
@@ -212,10 +205,8 @@ def _(
from typing import Callable, Union
from ty_extensions import Intersection, Not
class Foo: ...
def _(
c: Intersection[Callable[[Union[int, str]], int], int],
d: Intersection[int, Callable[[Union[int, str]], int]],
@@ -235,7 +226,6 @@ A nested `Callable` as one of the parameter types:
```py
from typing import Callable
def _(c: Callable[[Callable[[int], str]], int]):
reveal_type(c) # revealed: ((int, /) -> str, /) -> int
```
@@ -255,7 +245,6 @@ is a [gradual form] indicating that the type is consistent with any input signat
```py
from typing import Callable
def gradual_form(c: Callable[..., str]):
reveal_type(c) # revealed: (...) -> str
```
@@ -267,7 +256,6 @@ Using `Concatenate` as the first argument to `Callable`:
```py
from typing_extensions import Callable, Concatenate
def _(c: Callable[Concatenate[int, str, ...], int]):
# TODO: Should reveal the correct signature
reveal_type(c) # revealed: (...) -> int
@@ -318,7 +306,6 @@ Using a `ParamSpec` in a `Callable` annotation:
```py
from typing_extensions import Callable
def _[**P1](c: Callable[P1, int]):
reveal_type(P1.args) # revealed: P1@_.args
reveal_type(P1.kwargs) # revealed: P1@_.kwargs
@@ -333,7 +320,6 @@ from typing_extensions import ParamSpec
P2 = ParamSpec("P2")
def _(c: Callable[P2, int]):
reveal_type(c) # revealed: (**P2@_) -> int
```
@@ -347,7 +333,6 @@ from typing_extensions import Callable, TypeVarTuple
Ts = TypeVarTuple("Ts")
def _(c: Callable[[int, *Ts], int]):
# TODO: Should reveal the correct signature
reveal_type(c) # revealed: (...) -> int
@@ -358,7 +343,6 @@ And, using the legacy syntax using `Unpack`:
```py
from typing_extensions import Unpack
def _(c: Callable[[int, Unpack[Ts]], int]):
# TODO: Should reveal the correct signature
reveal_type(c) # revealed: (...) -> int
@@ -369,7 +353,6 @@ def _(c: Callable[[int, Unpack[Ts]], int]):
```py
from typing import Callable
def _(c: Callable[[int], int]):
reveal_type(c.__init__) # revealed: bound method object.__init__() -> None
reveal_type(c.__class__) # revealed: type
@@ -396,7 +379,6 @@ class MyCallable:
def __call__(self) -> None:
pass
f_wrong(MyCallable()) # raises `AttributeError` at runtime
```
@@ -406,7 +388,6 @@ of the attribute first:
```py
from inspect import getattr_static
def f_okay(c: Callable[[], None]):
if hasattr(c, "__qualname__"):
reveal_type(c.__qualname__) # revealed: object
@@ -433,16 +414,13 @@ def f_okay(c: Callable[[], None]):
```py
from ty_extensions import into_callable
class Base:
def __init__(self) -> None:
pass
class A(Base):
pass
# revealed: () -> A
reveal_type(into_callable(A))
```

View File

@@ -23,14 +23,11 @@ In (regular) source files, annotations are *not* deferred. This also tests that
```py
from __future__ import with_statement as annotations
# error: [unresolved-reference]
def get_foo() -> Foo: ...
class Foo: ...
reveal_type(get_foo()) # revealed: Unknown
```
@@ -41,14 +38,11 @@ If `__future__.annotations` is imported, annotations *are* deferred.
```py
from __future__ import annotations
def get_foo() -> Foo:
return Foo()
class Foo: ...
reveal_type(get_foo()) # revealed: Foo
```
@@ -62,7 +56,6 @@ python-version = "3.12"
```py
from __future__ import annotations
class Foo:
this: Foo
# error: [unresolved-reference]
@@ -84,11 +77,9 @@ class Foo:
def f(self, x: Foo):
return self
# error: [unresolved-reference]
def g(self) -> Bar:
return self
# error: [unresolved-reference]
def h[T: Bar](self):
pass
@@ -117,7 +108,6 @@ class Foo:
def h[T: Bar]():
# error: [unresolved-reference]
return Bar()
type Baz = Foo
```
@@ -142,7 +132,6 @@ class Foo:
# error: [unresolved-reference]
def f(self, x: Foo):
reveal_type(x) # revealed: Unknown
# error: [unresolved-reference]
def g(self) -> Foo:
_: Foo = self
@@ -156,11 +145,9 @@ class Foo:
# error: [unresolved-reference]
def f(self, x: Foo):
return self
# error: [unresolved-reference]
def g(self) -> Bar:
return self
# error: [unresolved-reference]
def h[T: Bar](self):
pass
@@ -189,10 +176,8 @@ class Foo:
def h[T: Bar]():
# error: [unresolved-reference]
return Bar()
type Qux = Foo
def _():
class C:
# error: [unresolved-reference]
@@ -207,11 +192,9 @@ def _():
```py
from __future__ import annotations
class A(B): # error: [unresolved-reference]
pass
class B:
pass
```
@@ -232,7 +215,6 @@ class B: ...
def f(mode: int = ParseMode.test):
pass
class ParseMode:
test = 1
```
@@ -264,7 +246,6 @@ def f(mode: int = NeverDefined.test): ...
class Foo(metaclass=SomeMeta):
pass
class SomeMeta(type):
pass
```
@@ -294,7 +275,6 @@ class Foo(metaclass=NeverDefined): ...
# error: [unresolved-reference]
f = lambda x=Foo(): x
class Foo:
pass
```

View File

@@ -11,7 +11,6 @@ Numbers = list[int]
# this as `list[int]` is more helpful, though:
reveal_type(Numbers) # revealed: <class 'list[int]'>
def _(numbers: Numbers) -> None:
reveal_type(numbers) # revealed: list[int]
```

View File

@@ -12,7 +12,6 @@ An annotation of `float` means `int | float`, so `int` is assignable to it:
def takes_float(x: float):
pass
def passes_int_to_float(x: int):
# no error!
takes_float(x)
@@ -32,12 +31,10 @@ It doesn't work the other way around:
def takes_int(x: int):
pass
def passes_float_to_int(x: float):
# error: [invalid-argument-type]
takes_int(x)
def assigns_float_to_int(x: float):
# error: [invalid-assignment]
y: int = x
@@ -60,41 +57,34 @@ to it (but not the other way around):
def takes_complex(x: complex):
pass
def passes_to_complex(x: float, y: int):
# no errors!
takes_complex(x)
takes_complex(y)
def assigns_to_complex(x: float, y: int):
# no errors!
a: complex = x
b: complex = y
def takes_int(x: int):
pass
def takes_float(x: float):
pass
def passes_complex(x: complex):
# error: [invalid-argument-type]
takes_int(x)
# error: [invalid-argument-type]
takes_float(x)
def assigns_complex(x: complex):
# error: [invalid-assignment]
y: int = x
# error: [invalid-assignment]
z: float = x
def f(x: complex):
reveal_type(x) # revealed: int | float | complex
```
@@ -108,7 +98,6 @@ be narrowed to `int` or `float`:
from typing_extensions import assert_type
from ty_extensions import JustFloat
def f(x: complex):
reveal_type(x) # revealed: int | float | complex

View File

@@ -9,10 +9,8 @@ import typing
from ty_extensions import AlwaysTruthy, AlwaysFalsy
from typing_extensions import Literal, Never
class A: ...
def _(
a: type[int],
b: AlwaysTruthy,
@@ -51,7 +49,6 @@ def _(
reveal_type(i_) # revealed: Unknown
reveal_type(j_) # revealed: Unknown
# Inspired by the conformance test suite at
# https://github.com/python/typing/blob/d4f39b27a4a47aac8b6d4019e1b0b5b3156fabdc/conformance/tests/aliases_implicit.py#L88-L122
B = [x for x in range(42)]
@@ -59,7 +56,6 @@ C = {x for x in range(42)}
D = {x: y for x, y in enumerate(range(42))}
E = (x for x in range(42))
def _(
b: B, # error: [invalid-type-form]
c: C, # error: [invalid-type-form]
@@ -78,13 +74,11 @@ def _(
def bar() -> None:
return None
def outer_sync(): # `yield` from is only valid syntax inside a synchronous function
def _(
a: (yield from [1]), # error: [invalid-type-form] "`yield from` expressions are not allowed in type expressions"
): ...
async def baz(): ...
async def outer_async(): # avoid unrelated syntax errors on `yield` and `await`
def _(
@@ -126,7 +120,6 @@ async def outer_async(): # avoid unrelated syntax errors on `yield` and `await`
reveal_type(p) # revealed: int | Unknown
reveal_type(q) # revealed: Unknown
class Mat:
def __init__(self, value: int):
self.value = value
@@ -134,7 +127,6 @@ class Mat:
def __matmul__(self, other) -> int:
return 42
def invalid_binary_operators(
a: "1" + "2", # error: [invalid-type-form] "Invalid binary operator `+` in type annotation"
b: 3 - 5.0, # error: [invalid-type-form] "Invalid binary operator `-` in type annotation"
@@ -194,12 +186,10 @@ def _(
reveal_type(h) # revealed: Unknown
reveal_type(i) # revealed: Unknown
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression: Did you mean `list[int]`?"
class name_0[name_2: [int]]:
pass
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression"
# error: [invalid-type-form] "Dict literals are not allowed in type expressions"
class name_4[name_1: [{}]]:
@@ -221,7 +211,6 @@ for this case:
```py
import datetime
def f(x: datetime): ... # error: [invalid-type-form]
```
@@ -236,7 +225,6 @@ class Image: ...
```py
from PIL import Image
def g(x: Image): ... # error: [invalid-type-form]
```

View File

@@ -18,19 +18,16 @@ a6: Literal[True]
a7: Literal[None]
a8: Literal[Literal[1]]
class Color(Enum):
RED = 0
GREEN = 1
BLUE = 2
b1: Literal[Color.RED]
MissingT = Enum("MissingT", {"MISSING": "MISSING"})
b2: Literal[MissingT.MISSING]
def f():
reveal_type(mode) # revealed: Literal["w", "r"]
reveal_type(a1) # revealed: Literal[26]
@@ -45,7 +42,6 @@ def f():
# TODO should be `Literal[MissingT.MISSING]`
reveal_type(b2) # revealed: @Todo(functional `Enum` syntax)
# error: [invalid-type-form]
invalid1: Literal[3 + 4]
# error: [invalid-type-form]
@@ -61,11 +57,9 @@ invalid4: Literal[
(1, 2, 3), # error: [invalid-type-form]
]
class NotAnEnum:
x: int = 1
# error: [invalid-type-form]
invalid5: Literal[NotAnEnum.x]
@@ -92,12 +86,10 @@ from enum import Enum
import mod
class E(Enum):
A = 1
B = 2
type SingleInt = Literal[1]
type SingleStr = Literal["foo"]
type SingleBytes = Literal[b"bar"]
@@ -113,7 +105,6 @@ type AnEnum2 = Literal[E.A, E.B]
type Bool1 = bool
type Bool2 = Literal[True, False]
def _(
single_int: Literal[SingleInt],
single_str: Literal[SingleStr],
@@ -158,12 +149,10 @@ type SingleInt = Literal[2]
from typing import Literal, TypeAlias
from enum import Enum
class E(Enum):
A = 1
B = 2
SingleInt: TypeAlias = Literal[1]
SingleStr: TypeAlias = Literal["foo"]
SingleBytes: TypeAlias = Literal[b"bar"]
@@ -176,7 +165,6 @@ AnEnum2: TypeAlias = Literal[E.A, E.B]
Bool1: TypeAlias = bool
Bool2: TypeAlias = Literal[True, False]
def _(
single_int: Literal[SingleInt],
single_str: Literal[SingleStr],
@@ -215,12 +203,10 @@ def _(
from typing import Literal
from enum import Enum
class E(Enum):
A = 1
B = 2
SingleInt = Literal[1]
SingleStr = Literal["foo"]
SingleBytes = Literal[b"bar"]
@@ -236,7 +222,6 @@ AnEnum2 = Literal[E.A, E.B]
Bool1 = bool
Bool2 = Literal[True, False]
def _(
single_int: Literal[SingleInt],
single_str: Literal[SingleStr],
@@ -273,7 +258,6 @@ the union of those types.
```py
from typing import Literal
def x(
a1: Literal[Literal[Literal[1, 2, 3], "foo"], 5, None],
a2: Literal["w"] | Literal["r"],
@@ -291,21 +275,15 @@ def x(
```py
from typing import Literal, Union
def foo(x: int) -> int:
return x + 1
def bar(s: str) -> str:
return s
class A: ...
class B: ...
def union_example(
x: Union[
# unknown type
@@ -355,7 +333,6 @@ from other import Literal
# error: [invalid-type-form] "Invalid subscript of object of type `_SpecialForm` in type expression"
a1: Literal[26]
def f():
reveal_type(a1) # revealed: Unknown
```
@@ -367,7 +344,6 @@ from typing_extensions import Literal
a1: Literal[26]
def f():
reveal_type(a1) # revealed: Literal[26]
```
@@ -377,7 +353,6 @@ def f():
```py
from typing import Literal
# error: [invalid-type-form] "`typing.Literal` requires at least one argument when used in a type expression"
def _(x: Literal):
reveal_type(x) # revealed: Unknown

View File

@@ -16,7 +16,6 @@ from typing_extensions import LiteralString
x: LiteralString
def f():
reveal_type(x) # revealed: LiteralString
```
@@ -55,7 +54,6 @@ Subclassing `LiteralString` leads to a runtime error.
```py
from typing_extensions import LiteralString
class C(LiteralString): ... # error: [invalid-base]
```
@@ -94,7 +92,6 @@ vice versa.
```py
from typing_extensions import Literal, LiteralString
def _(flag: bool):
foo_1: Literal["foo"] = "foo"
bar_1: LiteralString = foo_1 # fine
@@ -149,7 +146,6 @@ from typing import LiteralString
x: LiteralString = "foo"
def f():
reveal_type(x) # revealed: LiteralString
```

View File

@@ -9,11 +9,9 @@ interchangeably.
```py
from typing import NoReturn
def stop() -> NoReturn:
raise RuntimeError("no way")
# revealed: Never
reveal_type(stop())
```
@@ -30,7 +28,6 @@ a2: Never
b1: Any
b2: int
def f():
# revealed: Never
reveal_type(a1)

View File

@@ -9,7 +9,6 @@ from typing_extensions import NewType
UserId = NewType("UserId", int)
def _(user_id: UserId):
reveal_type(user_id) # revealed: UserId
```
@@ -39,12 +38,10 @@ Foo(Bar(Foo(42))) # allowed: `Bar` is a subtype of `int`.
Foo(True) # allowed: `bool` is a subtype of `int`.
Foo("forty-two") # error: [invalid-argument-type] "Argument is incorrect: Expected `int`, found `Literal["forty-two"]`"
def f(_: int): ...
def g(_: Foo): ...
def h(_: Bar): ...
f(42)
f(Foo(42))
f(Bar(Foo(42)))
@@ -63,14 +60,11 @@ h(Bar(Foo(42)))
```py
from typing_extensions import NewType
class Foo:
foo_member: str = "hello"
def foo_method(self) -> int:
return 42
Bar = NewType("Bar", Foo)
Baz = NewType("Baz", Bar)
baz = Baz(Bar(Foo()))
@@ -94,21 +88,16 @@ from ty_extensions import CallableTypeOf
Foo = NewType("Foo", int)
def _(obj: CallableTypeOf[Foo]):
reveal_type(obj) # revealed: (int, /) -> Foo
def f(_: Callable[[int], Foo]): ...
f(Foo)
map(Foo, [1, 2, 3])
def g(_: Callable[[str], Foo]): ...
g(Foo) # error: [invalid-argument-type]
```
@@ -123,23 +112,19 @@ i = N(42)
y: Callable[..., Any] = i # error: [invalid-assignment] "Object of type `N` is not assignable to `(...) -> Any`"
# error: [invalid-type-form] "Expected the first argument to `ty_extensions.CallableTypeOf` to be a callable object, but got an object of type `N`"
def f(x: CallableTypeOf[i]):
reveal_type(x) # revealed: Unknown
class SomethingCallable:
def __call__(self, a: str) -> bytes:
raise NotImplementedError
N2 = NewType("N2", SomethingCallable)
j = N2(SomethingCallable())
z: Callable[[str], bytes] = j # fine
def g(x: CallableTypeOf[j]):
reveal_type(x) # revealed: (a: str) -> bytes
```
@@ -149,7 +134,6 @@ def g(x: CallableTypeOf[j]):
```py
from typing_extensions import NewType
def _(name: str) -> None:
_ = NewType(name, int) # error: [invalid-newtype] "The first argument to `NewType` must be a string literal"
```
@@ -221,7 +205,6 @@ class Bar:
def __contains__(self, key: Foo) -> bool:
return True
reveal_type(Foo(42) + Bar()) # revealed: Foo
reveal_type(Bar() + Foo(42)) # revealed: Foo
reveal_type(Foo(42) < Bar()) # revealed: bool
@@ -296,16 +279,12 @@ type:
```py
from collections.abc import Callable
def f(_: Callable[[int | float], Foo]): ...
f(Foo)
def g(_: Callable[[int | float | complex], Bar]): ...
g(Bar)
```
@@ -342,7 +321,6 @@ class Bing:
def __contains__(self, key: Foo) -> bool:
return True
reveal_type(Foo(3.14) + Bing()) # revealed: Foo
reveal_type(Bing() + Foo(42)) # revealed: Foo
reveal_type(Foo(3.14) < Bing()) # revealed: bool
@@ -461,7 +439,6 @@ from typing import NewType
N = NewType("N", int)
def f(x: N):
reveal_type(isinstance(x, int)) # revealed: Literal[True]
```
@@ -493,7 +470,6 @@ from typing import NewType
X = NewType("X", int)
class Foo(X): ... # error: [invalid-base]
```
@@ -505,15 +481,12 @@ class Foo(X): ... # error: [invalid-base]
from enum import Enum
from typing import NewType
class Foo(Enum):
X = 0
Y = 1
N = NewType("N", Foo)
def f(x: N):
match x:
case Foo.X:
@@ -557,18 +530,14 @@ reveal_type(Bar(42)) # revealed: Bar
```py
from typing import NewType, Protocol, TypedDict
class Id(Protocol):
code: int
UserId = NewType("UserId", Id) # error: [invalid-newtype]
class Foo(TypedDict):
a: int
Bar = NewType("Bar", Foo) # error: [invalid-newtype]
```
@@ -609,15 +578,11 @@ from stub import N, A
n = N(A()) # fine
def f(x: A): ...
f(n) # fine
class Invalid: ...
bad = N(Invalid()) # error: [invalid-argument-type]
```

View File

@@ -12,7 +12,6 @@ a1: Optional[bool]
a2: Optional[Optional[bool]]
a3: Optional[None]
def f():
# revealed: int | None
reveal_type(a)
@@ -42,7 +41,6 @@ from typing_extensions import Optional
a: Optional[int]
def f():
# revealed: int | None
reveal_type(a)
@@ -53,7 +51,6 @@ def f():
```py
from typing import Optional
# error: [invalid-type-form] "`typing.Optional` requires exactly one argument when used in a type expression"
def f(x: Optional) -> None:
reveal_type(x) # revealed: Unknown

View File

@@ -14,7 +14,6 @@ python-version = "3.13"
```py
from typing import Self
class Shape:
def set_scale(self: Self, scale: float) -> Self:
reveal_type(self) # revealed: Self@set_scale
@@ -27,26 +26,21 @@ class Shape:
def inner() -> Self:
reveal_type(self) # revealed: Self@nested_func
return self
return inner()
def nested_func_without_enclosing_binding(self):
def inner(x: Self):
reveal_type(x) # revealed: Self@nested_func_without_enclosing_binding
inner(self)
reveal_type(Shape().nested_type()) # revealed: list[Shape]
reveal_type(Shape().nested_func()) # revealed: Shape
class Circle(Shape):
def set_scale(self: Self, scale: float) -> Self:
reveal_type(self) # revealed: Self@set_scale
return self
class Outer:
class Inner:
def foo(self: Self) -> Self:
@@ -68,7 +62,6 @@ python-version = "3.12"
```py
from typing import Self
class A:
def __init__(self):
reveal_type(self) # revealed: Self@__init__
@@ -110,7 +103,6 @@ class A:
@staticmethod
def a_staticmethod(x: int): ...
a = A()
reveal_type(a.implicit_self()) # revealed: A
@@ -131,11 +123,9 @@ Passing `self` implicitly also verifies the type:
```py
from typing import Never, Callable
class Strange:
def can_not_be_called(self: Never) -> None: ...
# error: [invalid-argument-type] "Argument to bound method `can_not_be_called` is incorrect: Expected `Never`, found `Strange`"
Strange().can_not_be_called()
```
@@ -157,7 +147,6 @@ The name `self` is not special in any way.
def some_decorator[**P, R](f: Callable[P, R]) -> Callable[P, R]:
return f
class B:
def name_does_not_matter(this) -> Self:
reveal_type(this) # revealed: Self@name_does_not_matter
@@ -195,14 +184,12 @@ class B:
@some_decorator
def decorated_static_method(self):
reveal_type(self) # revealed: Unknown
# TODO: On Python <3.10, this should ideally be rejected, because `staticmethod` objects were not callable.
@some_decorator
@staticmethod
def decorated_static_method_2(self):
reveal_type(self) # revealed: Unknown
reveal_type(B().name_does_not_matter()) # revealed: B
reveal_type(B().positional_only(1)) # revealed: B
reveal_type(B().keyword_only(x=1)) # revealed: B
@@ -210,7 +197,6 @@ reveal_type(B().decorated_method()) # revealed: B
reveal_type(B().a_property) # revealed: B
async def _():
reveal_type(await B().async_method()) # revealed: B
```
@@ -222,14 +208,12 @@ from typing import Self, Generic, TypeVar
T = TypeVar("T")
class G(Generic[T]):
def id(self) -> Self:
reveal_type(self) # revealed: Self@id
return self
reveal_type(G[int]().id()) # revealed: G[int]
reveal_type(G[str]().id()) # revealed: G[str]
```
@@ -240,18 +224,15 @@ Free functions and nested functions do not use implicit `Self`:
def not_a_method(self):
reveal_type(self) # revealed: Unknown
# error: [invalid-type-form]
def does_not_return_self(self) -> Self:
return self
class C:
def outer(self) -> None:
def inner(self):
reveal_type(self) # revealed: Unknown
reveal_type(not_a_method) # revealed: def not_a_method(self) -> Unknown
```
@@ -267,18 +248,15 @@ affect that subtitution. If we blindly substitute all occurrences of `Self`, we
```py
from typing import Self
class Foo[T]:
def foo(self: Self) -> T:
raise NotImplementedError
class Bar:
def bar(self: Self, x: Foo[Self]):
# revealed: bound method Foo[Self@bar].foo() -> Self@bar
reveal_type(x.foo)
def f[U: Bar](x: Foo[U]):
# revealed: bound method Foo[U@f].foo() -> U@f
reveal_type(x.foo)
@@ -294,12 +272,10 @@ python-version = "3.10"
```py
from typing_extensions import Self
class C:
def method(self: Self) -> Self:
return self
reveal_type(C().method()) # revealed: C
```
@@ -310,7 +286,6 @@ reveal_type(C().method()) # revealed: C
```py
from typing import Self
class Shape:
def foo(self: Self) -> Self:
return self
@@ -320,10 +295,8 @@ class Shape:
reveal_type(cls) # revealed: type[Self@bar]
return cls()
class Circle(Shape): ...
reveal_type(Shape().foo()) # revealed: Shape
reveal_type(Shape.bar()) # revealed: Shape
@@ -336,7 +309,6 @@ reveal_type(Circle.bar()) # revealed: Circle
```py
from typing import Self
class Shape:
def foo(self) -> Self:
return self
@@ -346,10 +318,8 @@ class Shape:
reveal_type(cls) # revealed: type[Self@bar]
return cls()
class Circle(Shape): ...
reveal_type(Shape().foo()) # revealed: Shape
reveal_type(Shape.bar()) # revealed: Shape
@@ -362,7 +332,6 @@ reveal_type(Circle.bar()) # revealed: Circle
```py
from typing import Self
class GenericShape[T]:
def foo(self) -> Self:
return self
@@ -377,10 +346,8 @@ class GenericShape[T]:
reveal_type(cls) # revealed: type[Self@baz]
return cls()
class GenericCircle[T](GenericShape[T]): ...
reveal_type(GenericShape().foo()) # revealed: GenericShape[Unknown]
reveal_type(GenericShape.bar()) # revealed: GenericShape[Unknown]
reveal_type(GenericShape[int].bar()) # revealed: GenericShape[int]
@@ -402,19 +369,16 @@ the return type should be the child's `Self` type variable, not the concrete chi
```py
from typing import Self
class Parent:
def copy(self) -> Self:
return self
class Child(Parent):
def copy(self) -> Self:
result = super().copy()
reveal_type(result) # revealed: Self@copy
return result
# When called on concrete types, Self is substituted correctly.
reveal_type(Child().copy()) # revealed: Child
```
@@ -424,13 +388,11 @@ The same applies to classmethods with `Self` return types:
```py
from typing import Self
class Parent:
@classmethod
def create(cls) -> Self:
return cls()
class Child(Parent):
@classmethod
def create(cls) -> Self:
@@ -438,7 +400,6 @@ class Child(Parent):
reveal_type(result) # revealed: Self@create
return result
# When called on concrete types, Self is substituted correctly.
reveal_type(Child.create()) # revealed: Child
```
@@ -451,7 +412,6 @@ TODO: The use of `Self` to annotate the `next_node` attribute should be
```py
from typing import Self
class LinkedList:
value: int
next_node: Self
@@ -462,7 +422,6 @@ class LinkedList:
# error: [invalid-return-type]
return self.next_node
reveal_type(LinkedList().next()) # revealed: LinkedList
```
@@ -473,10 +432,8 @@ from typing import Generic, TypeVar
T = TypeVar("T")
class C(Generic[T]):
foo: T
def method(self) -> None:
reveal_type(self) # revealed: Self@method
reveal_type(self.foo) # revealed: T@C
@@ -489,14 +446,11 @@ from typing import Self, Generic, TypeVar
T = TypeVar("T")
class Container(Generic[T]):
value: T
def set_value(self: Self, value: T) -> Self:
return self
int_container: Container[int] = Container[int]()
reveal_type(int_container) # revealed: Container[int]
reveal_type(int_container.set_value(1)) # revealed: Container[int]
@@ -512,31 +466,26 @@ a type that satisfies a bound.
```py
from typing import NewType
class Base: ...
class C[T: Base]:
x: T
def g(self) -> None:
pass
# Calling a method on a specialized instance should not produce an error
C[Base]().g()
# Test with a NewType bound
K = NewType("K", int)
class D[T: K]:
x: T
def h(self) -> None:
pass
# Calling a method on a specialized instance should not produce an error
D[K]().h()
```
@@ -550,7 +499,6 @@ TODO: <https://typing.python.org/en/latest/spec/generics.html#use-in-protocols>
```py
from typing import Self
class Shape:
def union(self: Self, other: Self | None):
reveal_type(other) # revealed: Self@union | None
@@ -564,12 +512,10 @@ This is a regression test for <https://github.com/astral-sh/ty/issues/1156>.
```py
from typing import Self
class Container[T = bytes]:
def __init__(self: Self, data: T | None = None) -> None:
self.data = data
reveal_type(Container()) # revealed: Container[bytes]
reveal_type(Container(1)) # revealed: Container[int]
reveal_type(Container("a")) # revealed: Container[str]
@@ -581,25 +527,20 @@ reveal_type(Container(b"a")) # revealed: Container[bytes]
```py
from typing import Self, TypeVar, Generic
class Container[T = bytes]:
def method(self) -> Self:
return self
def _(c: Container[str], d: Container):
reveal_type(c.method()) # revealed: Container[str]
reveal_type(d.method()) # revealed: Container[bytes]
T = TypeVar("T", default=bytes)
class LegacyContainer(Generic[T]):
def method(self) -> Self:
return self
def _(c: LegacyContainer[str], d: LegacyContainer):
reveal_type(c.method()) # revealed: LegacyContainer[str]
reveal_type(d.method()) # revealed: LegacyContainer[bytes]
@@ -614,15 +555,12 @@ from typing import Self, Generic, TypeVar
T = TypeVar("T")
# error: [invalid-type-form]
def x(s: Self): ...
# error: [invalid-type-form]
b: Self
# TODO: "Self" cannot be used in a function with a `self` or `cls` parameter that has a type annotation other than "Self"
class Foo:
# TODO: This `self: T` annotation should be rejected because `T` is not `Self`
@@ -640,14 +578,11 @@ class Foo:
# error: [invalid-return-type]
return Foo()
class Bar(Generic[T]): ...
# error: [invalid-type-form]
class Baz(Bar[Self]): ...
class MyMetaclass(type):
# TODO: reject the Self usage. because self cannot be used within a metaclass.
def __new__(cls) -> Self:
@@ -669,11 +604,9 @@ from __future__ import annotations
from typing import final
@final
class Disjoint: ...
class Explicit:
# TODO: We could emit a warning if the annotated type of `self` is disjoint from `Explicit`
def bad(self: Disjoint) -> None:
@@ -682,18 +615,15 @@ class Explicit:
def forward(self: Explicit) -> None:
reveal_type(self) # revealed: Explicit
# error: [invalid-argument-type] "Argument to bound method `bad` is incorrect: Expected `Disjoint`, found `Explicit`"
Explicit().bad()
Explicit().forward()
class ExplicitGeneric[T]:
def special(self: ExplicitGeneric[int]) -> None:
reveal_type(self) # revealed: ExplicitGeneric[int]
ExplicitGeneric[int]().special()
# TODO: this should be an `invalid-argument-type` error
@@ -708,7 +638,6 @@ specific type of the bound parameter.
```py
from typing import Self
class C:
def instance_method(self, other: Self) -> Self:
return self
@@ -717,16 +646,13 @@ class C:
def class_method(cls) -> Self:
return cls()
# revealed: bound method C.instance_method(other: C) -> C
reveal_type(C().instance_method)
# revealed: bound method <class 'C'>.class_method() -> C
reveal_type(C.class_method)
class D(C): ...
# revealed: bound method D.instance_method(other: D) -> D
reveal_type(D().instance_method)
# revealed: bound method <class 'D'>.class_method() -> D
@@ -740,16 +666,13 @@ bound at `C.f`.
from typing import Self
from ty_extensions import generic_context
class C[T]():
def f(self: Self):
def b(x: Self):
reveal_type(x) # revealed: Self@f
# revealed: None
reveal_type(generic_context(b))
# revealed: ty_extensions.GenericContext[Self@f]
reveal_type(generic_context(C.f))
```
@@ -761,16 +684,13 @@ Even if the `Self` annotation appears first in the nested function, it is the me
from typing import Self
from ty_extensions import generic_context
class C:
def f(self: "C"):
def b(x: Self):
reveal_type(x) # revealed: Self@f
# revealed: None
reveal_type(generic_context(b))
# revealed: None
reveal_type(generic_context(C.f))
```
@@ -782,11 +702,9 @@ This makes sure that we don't bind `self` if it's not a positional parameter:
```py
from ty_extensions import CallableTypeOf
class C:
def method(*args, **kwargs) -> None: ...
def _(c: CallableTypeOf[C().method]):
reveal_type(c) # revealed: (...) -> None
```

View File

@@ -12,13 +12,11 @@ from typing_extensions import TypeVarTuple
Ts = TypeVarTuple("Ts")
def append_int(*args: *Ts) -> tuple[*Ts, int]:
reveal_type(args) # revealed: @Todo(PEP 646)
return (*args, 1)
# TODO should be tuple[Literal[True], Literal["a"], int]
reveal_type(append_int(True, "a")) # revealed: tuple[@Todo(PEP 646), ...]
```

View File

@@ -10,7 +10,6 @@ All of the following symbols can be mapped one-to-one with the actual type:
```py
import typing
def f(
list_bare: typing.List,
list_parametrized: typing.List[int],
@@ -66,7 +65,6 @@ In case the incorrect number of type arguments is passed, a diagnostic is given.
```py
import typing
def f(
# error: [invalid-type-form] "Legacy alias `typing.List` expected exactly 1 argument, got 2"
incorrect_list: typing.List[int, int],
@@ -122,31 +120,23 @@ from ty_extensions import reveal_mro
### Built-ins
####################
class ListSubclass(typing.List): ...
# revealed: (<class 'ListSubclass'>, <class 'list[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(ListSubclass)
class DictSubclass(typing.Dict): ...
# revealed: (<class 'DictSubclass'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(DictSubclass)
class SetSubclass(typing.Set): ...
# revealed: (<class 'SetSubclass'>, <class 'set[Unknown]'>, <class 'MutableSet[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(SetSubclass)
class FrozenSetSubclass(typing.FrozenSet): ...
# revealed: (<class 'FrozenSetSubclass'>, <class 'frozenset[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(FrozenSetSubclass)
@@ -154,38 +144,28 @@ reveal_mro(FrozenSetSubclass)
### `collections`
####################
class ChainMapSubclass(typing.ChainMap): ...
# revealed: (<class 'ChainMapSubclass'>, <class 'ChainMap[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(ChainMapSubclass)
class CounterSubclass(typing.Counter): ...
# revealed: (<class 'CounterSubclass'>, <class 'Counter[Unknown]'>, <class 'dict[Unknown, int]'>, <class 'MutableMapping[Unknown, int]'>, <class 'Mapping[Unknown, int]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(CounterSubclass)
class DefaultDictSubclass(typing.DefaultDict): ...
# revealed: (<class 'DefaultDictSubclass'>, <class 'defaultdict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(DefaultDictSubclass)
class DequeSubclass(typing.Deque): ...
# revealed: (<class 'DequeSubclass'>, <class 'deque[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(DequeSubclass)
class OrderedDictSubclass(typing.OrderedDict): ...
# revealed: (<class 'OrderedDictSubclass'>, <class 'OrderedDict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
reveal_mro(OrderedDictSubclass)
```

View File

@@ -35,7 +35,6 @@ def f(v: tuple[int, "str"]):
def f(v: "Foo"):
reveal_type(v) # revealed: Foo
class Foo: ...
```
@@ -53,7 +52,6 @@ def f(v: "Foo"):
def f(v: int | "Foo"):
reveal_type(v) # revealed: int | Foo
class Foo: ...
```
@@ -62,12 +60,10 @@ class Foo: ...
```py
from typing import Literal
def f1(v: Literal["Foo", "Bar"], w: 'Literal["Foo", "Bar"]'):
reveal_type(v) # revealed: Literal["Foo", "Bar"]
reveal_type(w) # revealed: Literal["Foo", "Bar"]
class Foo: ...
```
@@ -108,7 +104,6 @@ def f1(
```py
from typing import Literal
def f(v: Literal["a", r"b", b"c", "d" "e", "\N{LATIN SMALL LETTER F}", "\x67", """h"""]):
reveal_type(v) # revealed: Literal["a", "b", "de", "f", "g", "h", b"c"]
```
@@ -118,14 +113,12 @@ def f(v: Literal["a", r"b", b"c", "d" "e", "\N{LATIN SMALL LETTER F}", "\x67", "
```py
MyType = int
class Aliases:
MyType = str
forward: "MyType" = "value"
not_forward: MyType = "value"
reveal_type(Aliases.forward) # revealed: str
reveal_type(Aliases.not_forward) # revealed: str
```
@@ -139,10 +132,8 @@ c: "Foo"
# error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to `Foo`"
d: "Foo" = 1
class Foo: ...
c = Foo()
reveal_type(a) # revealed: Literal[1]
@@ -220,7 +211,6 @@ def valid(
reveal_type(a1) # revealed: int | str
reveal_type(a2) # revealed: int | str
def invalid(
# error: [invalid-syntax-in-forward-annotation]
a1: """

View File

@@ -15,7 +15,6 @@ a4: Union[Union[bytes, str]]
a5: Union[int]
a6: Union[()]
def f():
# revealed: int | str
reveal_type(a)
@@ -56,7 +55,6 @@ from typing_extensions import Union
a: Union[int, str]
def f():
# revealed: int | str
reveal_type(a)
@@ -67,7 +65,6 @@ def f():
```py
from typing import Union
# error: [invalid-type-form] "`typing.Union` requires at least one argument when used in a type expression"
def f(x: Union) -> None:
reveal_type(x) # revealed: Unknown
@@ -83,7 +80,6 @@ python-version = "3.10"
```py
X = int | str
def f(y: X):
reveal_type(y) # revealed: int | str
```

View File

@@ -12,44 +12,34 @@ P = ParamSpec("P")
Ts = TypeVarTuple("Ts")
R_co = TypeVar("R_co", covariant=True)
def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
reveal_type(args) # revealed: tuple[@Todo(`Unpack[]` special form), ...]
return args
def i(callback: Callable[Concatenate[int, P], R_co], *args: P.args, **kwargs: P.kwargs) -> R_co:
reveal_type(args) # revealed: P@i.args
reveal_type(kwargs) # revealed: P@i.kwargs
return callback(42, *args, **kwargs)
class Foo:
def method(self, x: Self):
reveal_type(x) # revealed: Self@method
def ex2(msg: str):
def wrapper(fn: Callable[P, R_co]) -> Callable[P, R_co]:
def wrapped(*args: P.args, **kwargs: P.kwargs) -> R_co:
print(msg)
return fn(*args, **kwargs)
return wrapped
return wrapper
def ex3(msg: str):
P = ParamSpec("P")
def wrapper(fn: Callable[P, R_co]) -> Callable[P, R_co]:
def wrapped(*args: P.args, **kwargs: P.kwargs) -> R_co:
print(msg)
return fn(*args, **kwargs)
return wrapped
return wrapper
```
@@ -60,7 +50,6 @@ One thing that is supported is error messages for using special forms in type ex
```py
from typing_extensions import Unpack, TypeGuard, TypeIs, Concatenate, ParamSpec, Generic
def _(
a: Unpack, # error: [invalid-type-form] "`typing.Unpack` requires exactly one argument when used in a type expression"
b: TypeGuard, # error: [invalid-type-form] "`typing.TypeGuard` requires exactly one argument when used in a type expression"
@@ -88,28 +77,14 @@ from typing import Callable
from typing_extensions import Self, Unpack, TypeGuard, TypeIs, Concatenate, Generic
from ty_extensions import reveal_mro
class A(Self): ... # error: [invalid-base]
class B(Unpack): ... # error: [invalid-base]
class C(TypeGuard): ... # error: [invalid-base]
class D(TypeIs): ... # error: [invalid-base]
class E(Concatenate): ... # error: [invalid-base]
class F(Callable): ...
class G(Generic): ... # error: [invalid-base] "Cannot inherit from plain `Generic`"
reveal_mro(F) # revealed: (<class 'F'>, @Todo(Support for Callable as a base class), <class 'object'>)
```
@@ -130,7 +105,6 @@ T = TypeVar("T")
# error: [invalid-type-form] "Special form `typing.TypeAlias` expected no type parameter"
X: TypeAlias[T] = int
class Foo[T]:
# error: [invalid-type-form] "Special form `typing.Self` expected no type parameter"
# error: [invalid-type-form] "Special form `typing.Self` expected no type parameter"

View File

@@ -14,6 +14,5 @@ MyTypedDict = typing.TypedDict("MyTypedDict", {"foo": int})
MyNamedTuple1 = typing.NamedTuple("MyNamedTuple1", [("foo", int)])
MyNamedTuple2 = collections.namedtuple("MyNamedTuple2", ["foo"])
def f(a: MyEnum, b: MyTypedDict, c: MyNamedTuple1, d: MyNamedTuple2): ...
```

View File

@@ -11,7 +11,6 @@ from typing_extensions import Final, ReadOnly, TypedDict
X: Final = 42
Y: Final[int] = 42
class Bar(TypedDict):
z: ReadOnly[bytes]
```
@@ -23,7 +22,6 @@ One thing that is supported is error messages for using type qualifiers in type
```py
from typing_extensions import Final, ClassVar, Required, NotRequired, ReadOnly
def _(
# error: [invalid-type-form] "Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)"
a: Final | int,
@@ -44,12 +42,7 @@ You can't inherit from a type qualifier.
```py
from typing_extensions import Final, ClassVar, Required, NotRequired, ReadOnly
class A(Final): ... # error: [invalid-base]
class B(ClassVar): ... # error: [invalid-base]
class C(ReadOnly): ... # error: [invalid-base]
```

View File

@@ -239,12 +239,10 @@ python-version = "3.12"
```py
from typing import Any
class X[T]:
def __init__(self, value: T):
self.value = value
x1: X[int] = X(1)
reveal_type(x1) # revealed: X[int]
@@ -254,16 +252,13 @@ reveal_type(x2) # revealed: X[int | None]
x3: X[int | None] | None = X(1)
reveal_type(x3) # revealed: X[int | None]
def _[T](x1: X[T]):
x2: X[T | int] = X(x1.value)
reveal_type(x2) # revealed: X[T@_ | int]
x4: X[Any] = X(1)
reveal_type(x4) # revealed: X[Any]
def _(flag: bool):
x5: X[int | None] = X(1) if flag else X(2)
reveal_type(x5) # revealed: X[int | None]
@@ -272,12 +267,10 @@ def _(flag: bool):
```py
from dataclasses import dataclass
@dataclass
class Y[T]:
value: T
y1 = Y(value=1)
reveal_type(y1) # revealed: Y[int]
@@ -292,7 +285,6 @@ class Z[T]:
def __new__(cls, value: T):
return super().__new__(cls)
z1 = Z(1)
reveal_type(z1) # revealed: Z[int]
@@ -360,10 +352,8 @@ from __future__ import annotations
x: Foo
class Foo: ...
x = Foo()
reveal_type(x) # revealed: Foo
```
@@ -389,10 +379,8 @@ python-version = "3.14"
```py
x: Foo
class Foo: ...
x = Foo()
reveal_type(x) # revealed: Foo
```
@@ -416,11 +404,9 @@ python-version = "3.12"
```py
from typing import Literal, Sequence
def f[T](x: T) -> list[T]:
return [x]
x1 = f("a")
reveal_type(x1) # revealed: list[str]
@@ -442,11 +428,9 @@ x6: list[int] = f("a")
# error: [invalid-assignment] "Object of type `list[str]` is not assignable to `tuple[int]`"
x7: tuple[int] = f("a")
def f2[T: int](x: T) -> T:
return x
x8: int = f2(True)
reveal_type(x8) # revealed: Literal[True]
@@ -469,15 +453,12 @@ A function's arguments are also inferred using the type context:
```py
from typing import TypedDict
class TD(TypedDict):
x: int
def f[T](x: list[T]) -> T:
return x[0]
a: TD = f([{"x": 0}, {"x": 1}])
reveal_type(a) # revealed: TD
@@ -502,15 +483,12 @@ But not in a way that leads to assignability errors:
```py
from typing import TypedDict, Any
class TD(TypedDict, total=False):
x: str
class TD2(TypedDict):
x: str
def f(self, dt: dict[str, Any], key: str):
x1: TD = dt.get(key, {})
reveal_type(x1) # revealed: Any
@@ -547,19 +525,15 @@ python-version = "3.14"
```py
from typing import Any
def f[T](x: T) -> list[T]:
return [x]
def f2[T](x: T) -> list[T] | None:
return [x]
def f3[T](x: T) -> list[T] | dict[T, T]:
return [x]
a = f(1)
reveal_type(a) # revealed: list[int]
@@ -590,37 +564,29 @@ We only prefer the declared type if it is in non-covariant position.
class Bivariant[T]:
pass
class Covariant[T]:
def pop(self) -> T:
raise NotImplementedError
class Contravariant[T]:
def push(self, value: T) -> None:
pass
class Invariant[T]:
x: T
def bivariant[T](x: T) -> Bivariant[T]:
return Bivariant()
def covariant[T](x: T) -> Covariant[T]:
return Covariant()
def contravariant[T](x: T) -> Contravariant[T]:
return Contravariant()
def invariant[T](x: T) -> Invariant[T]:
return Invariant()
x1 = bivariant(1)
x2 = covariant(1)
x3 = contravariant(1)
@@ -648,7 +614,6 @@ class X[T]:
def pop(self) -> T:
raise NotImplementedError
x1: X[int | None] = X()
reveal_type(x1) # revealed: X[None]
```
@@ -685,20 +650,16 @@ reveal_type(x5) # revealed: list[Iterable[Any]]
x6: Iterable[list[Any]] = [[1, 2, 3]]
reveal_type(x6) # revealed: list[list[Any]]
class X[T]:
value: T
def __init__(self, value: T): ...
class A[T](X[T]): ...
def a[T](value: T) -> A[T]:
return A(value)
x7: A[object] = A(1)
reveal_type(x7) # revealed: A[object]
@@ -711,11 +672,9 @@ reveal_type(x9) # revealed: A[object]
x10: X[object] | None = a(1)
reveal_type(x10) # revealed: A[object]
def f[T](x: T) -> list[list[T]]:
return [[x]]
x11: Sequence[Sequence[Any]] = f(1)
reveal_type(x11) # revealed: list[list[int]]
@@ -736,35 +695,28 @@ python-version = "3.12"
```py
from typing import reveal_type, TypedDict
def identity[T](x: T) -> T:
return x
def _(narrow: dict[str, str], target: list[str] | dict[str, str] | None):
target = identity(narrow)
reveal_type(target) # revealed: dict[str, str]
def _(narrow: list[str], target: list[str] | dict[str, str] | None):
target = identity(narrow)
reveal_type(target) # revealed: list[str]
def _(narrow: list[str] | dict[str, str], target: list[str] | dict[str, str] | None):
target = identity(narrow)
reveal_type(target) # revealed: list[str] | dict[str, str]
class TD(TypedDict):
x: int
def _(target: list[TD] | dict[str, TD] | None):
target = identity([{"x": 1}])
reveal_type(target) # revealed: list[TD]
def _(target: list[TD] | dict[str, TD] | None):
target = identity({"x": {"x": 1}})
reveal_type(target) # revealed: dict[str, TD]
@@ -781,11 +733,9 @@ python-version = "3.12"
def identity[T](x: T) -> T:
return x
def lst[T](x: T) -> list[T]:
return [x]
def _(i: int):
a: int | None = i
b: int | None = identity(i)
@@ -815,11 +765,9 @@ def _(i: int):
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: list[Unknown]
def f[T](x: list[T]) -> T:
return x[0]
def _(a: int, b: str, c: int | str):
x1: int = f(lst(a))
reveal_type(x1) # revealed: int

View File

@@ -23,17 +23,14 @@ class C:
def __isub__(self, other: int) -> str:
return "Hello, world!"
x = C()
x -= 1
reveal_type(x) # revealed: str
class C:
def __iadd__(self, other: str) -> int:
return 1
x = C()
x += "Hello"
reveal_type(x) # revealed: int
@@ -48,7 +45,6 @@ class C:
def __isub__(self, other: str) -> int:
return 42
x = C()
# error: [unsupported-operator] "Operator `-=` is not supported between objects of type `C` and `Literal[1]`"
x -= 1
@@ -62,12 +58,9 @@ reveal_type(x) # revealed: int
def _(flag: bool):
class Foo:
if flag:
def __iadd__(self, other: int) -> str:
return "Hello, world!"
else:
def __iadd__(self, other: int) -> int:
return 42
@@ -83,7 +76,6 @@ def _(flag: bool):
def _(flag: bool):
class Foo:
if flag:
def __iadd__(self, other: str) -> int:
return 42
@@ -102,9 +94,7 @@ def _(flag: bool):
class Foo:
def __add__(self, other: str) -> str:
return "Hello, world!"
if flag:
def __iadd__(self, other: str) -> int:
return 42
@@ -121,9 +111,7 @@ def _(flag1: bool, flag2: bool):
class Foo:
def __add__(self, other: int) -> str:
return "Hello, world!"
if flag1:
def __iadd__(self, other: int) -> int:
return 42
@@ -160,9 +148,7 @@ def f(flag: bool, flag2: bool):
class Foo:
def __add__(self, other: int) -> str:
return "Hello, world!"
if flag:
def __iadd__(self, other: int) -> int:
return 42
@@ -189,10 +175,8 @@ class Meta(type):
def __iadd__(cls, other: int) -> str:
return ""
class C(metaclass=Meta): ...
cls = C
cls += 1

View File

@@ -6,7 +6,6 @@
async def retrieve() -> int:
return 42
async def main():
result = await retrieve()
@@ -20,11 +19,9 @@ from typing import TypeVar
T = TypeVar("T")
async def persist(x: T) -> T:
return x
async def f(x: int):
result = await persist(x)
@@ -39,11 +36,9 @@ async def f(x: int):
import asyncio
import concurrent.futures
def blocking_function() -> int:
return 42
async def main():
loop = asyncio.get_event_loop()
with concurrent.futures.ThreadPoolExecutor() as pool:
@@ -56,11 +51,9 @@ async def main():
```py
import asyncio
async def f() -> int:
return 1
async def main():
task = asyncio.create_task(f())
@@ -74,11 +67,9 @@ async def main():
```py
import asyncio
async def task(name: str) -> int:
return len(name)
async def main():
(a, b) = await asyncio.gather(
task("A"),
@@ -122,7 +113,6 @@ final type of the `await` expression, we retrieve that third argument of the `Ge
```py
from typing import Generator
def _():
result = yield from retrieve().__await__()
reveal_type(result) # revealed: int
@@ -137,6 +127,5 @@ not just `Unknown`:
async def f():
pass
reveal_type(f()) # revealed: CoroutineType[Any, Any, Unknown]
```

File diff suppressed because it is too large Load Diff

View File

@@ -18,11 +18,9 @@ python-version = "3.12"
```py
from typing import Literal
def list1[T](x: T) -> list[T]:
return [x]
l1: list[Literal[1]] = list1(1)
reveal_type(l1) # revealed: list[Literal[1]]
@@ -32,7 +30,6 @@ reveal_type(l2) # revealed: list[int]
l3: list[int | str] | None = list1(1)
reveal_type(l3) # revealed: list[int | str]
def _(l: list[int] | None = None):
l1 = l or list()
reveal_type(l1) # revealed: (list[int] & ~AlwaysFalsy) | list[Unknown]
@@ -41,18 +38,10 @@ def _(l: list[int] | None = None):
# it would be better if this were `list[int]`? (https://github.com/astral-sh/ty/issues/136)
reveal_type(l2) # revealed: (list[int] & ~AlwaysFalsy) | list[Unknown]
def f[T](x: T, cond: bool) -> T | list[T]:
return x if cond else [x]
l5: int | list[int] = f(1, True)
a: list[int] = [1, 2, *(3, 4, 5)]
reveal_type(a) # revealed: list[int]
b: list[list[int]] = [[1], [2], *([3], [4])]
reveal_type(b) # revealed: list[list[int]]
```
`typed_dict.py`:
@@ -60,11 +49,9 @@ reveal_type(b) # revealed: list[list[int]]
```py
from typing import TypedDict
class TD(TypedDict):
x: int
d1 = {"x": 1}
d2: TD = {"x": 1}
d3: dict[str, int] = {"x": 1}
@@ -76,11 +63,9 @@ reveal_type(d2) # revealed: TD
reveal_type(d3) # revealed: dict[str, int]
reveal_type(d4) # revealed: TD
def _() -> TD:
return {"x": 1}
def _() -> TD:
# error: [missing-typed-dict-key] "Missing required key 'x' in TypedDict `TD` constructor"
# error: [invalid-return-type]
@@ -97,15 +82,12 @@ python-version = "3.12"
```py
from typing import overload, Callable
def list1[T](x: T) -> list[T]:
return [x]
def get_data() -> dict | None:
return {}
def wrap_data() -> list[dict]:
if not (res := get_data()):
return list1({})
@@ -115,18 +97,15 @@ def wrap_data() -> list[dict]:
# by bidirectional type inference using the annotated return type, and the type of `res` is not used.
return list1(res)
def wrap_data2() -> list[dict] | None:
if not (res := get_data()):
return None
reveal_type(list1(res)) # revealed: list[dict[Unknown, Unknown] & ~AlwaysFalsy]
return list1(res)
def deco[T](func: Callable[[], T]) -> Callable[[], T]:
return func
def outer() -> Callable[[], list[dict]]:
@deco
def inner() -> list[dict]:
@@ -134,10 +113,8 @@ def outer() -> Callable[[], list[dict]]:
return list1({})
reveal_type(list1(res)) # revealed: list[dict[Unknown, Unknown] & ~AlwaysFalsy]
return list1(res)
return inner
@overload
def f(x: int) -> list[int]: ...
@overload
@@ -149,19 +126,15 @@ def f(x: int | str) -> list[int] | list[str]:
else:
return list1(x)
reveal_type(f(1)) # revealed: list[int]
reveal_type(f("a")) # revealed: list[str]
async def g() -> list[int | str]:
return list1(1)
def h[T](x: T, cond: bool) -> T | list[T]:
return i(x, cond)
def i[T](x: T, cond: bool) -> T | list[T]:
return x if cond else [x]
```
@@ -181,7 +154,6 @@ Function parameter annotations:
```py
def b(x: list[Literal[1]]): ...
b([1])
```
@@ -192,7 +164,6 @@ class C:
def __init__(self, x: list[Literal[1]]): ...
def foo(self, x: list[Literal[1]]): ...
C([1]).foo([1])
```
@@ -210,7 +181,6 @@ class E:
a: list[Literal[1]]
b: list[Literal[1]]
def _(e: E):
e.a = [1]
E.b = [1]
@@ -235,7 +205,6 @@ Both meta and class/instance attribute annotations are used as type context:
```py
from typing import Literal, Any
class DataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> list[Literal[1]]:
return []
@@ -243,11 +212,9 @@ class DataDescriptor:
def __set__(self, instance: object, value: list[Literal[1]]) -> None:
pass
def lst[T](x: T) -> list[T]:
return [x]
def _(flag: bool):
class Meta(type):
if flag:
@@ -269,19 +236,15 @@ For union targets, each element of the union is considered as a separate type co
```py
from typing import Literal
class X:
x: list[int | str]
class Y:
x: list[int | None]
def lst[T](x: T) -> list[T]:
return [x]
def _(xy: X | Y):
xy.x = lst(1)
```
@@ -300,14 +263,12 @@ calls:
def f[T](x: T) -> list[T]:
return [x]
class A:
def __new__(cls, value: list[int | str]):
return super().__new__(cls, value)
def __init__(self, value: list[int | None]): ...
A(f(1))
# error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `list[int | str]`, found `list[list[Unknown]]`"
@@ -328,7 +289,6 @@ The type context is propagated through both branches of conditional expressions:
def f[T](x: T) -> list[T]:
raise NotImplementedError
def _(flag: bool):
x1 = f(1) if flag else f(2)
reveal_type(x1) # revealed: list[int]
@@ -344,24 +304,19 @@ The key and value parameters types are used as type context for `__setitem__` du
```py
from typing import TypedDict
class Bar(TypedDict):
baz: float
def _(x: dict[str, Bar]):
x["foo"] = reveal_type({"baz": 2}) # revealed: Bar
class X:
def __setitem__(self, key: Bar, value: Bar): ...
def _(x: X):
# revealed: Bar
x[reveal_type({"baz": 1})] = reveal_type({"baz": 2}) # revealed: Bar
# TODO: Support type context with union subscripting.
def _(x: X | dict[Bar, Bar]):
# error: [invalid-assignment]
@@ -384,7 +339,6 @@ Diagnostics unrelated to the type-context are only reported once:
def f[T](x: T) -> list[T]:
return [x]
def a(x: list[bool], y: list[bool]): ...
def b(x: list[int], y: list[int]): ...
def c(x: list[int], y: list[int]): ...
@@ -425,15 +379,12 @@ def _(a: object, b: object, flag: bool):
```py
from typing import TypedDict
class TD(TypedDict):
y: int
class X:
td: TD
def _(x: X, flag: bool):
if flag:
y = 1

View File

@@ -109,7 +109,6 @@ def _(a: bool):
```py
import random
def _(a: bool):
def lhs_is_int(x: int):
reveal_type(x | a) # revealed: int

View File

@@ -11,11 +11,8 @@ python-version = "3.10"
```py
class A: ...
class B: ...
reveal_type(A | B) # revealed: <types.UnionType special-form 'A | B'>
```
@@ -28,11 +25,8 @@ python-version = "3.9"
```py
class A: ...
class B: ...
# error: "Operator `|` is not supported between objects of type `<class 'A'>` and `<class 'B'>`"
reveal_type(A | B) # revealed: Unknown
```
@@ -46,23 +40,16 @@ python-version = "3.12"
```py
class A: ...
class B: ...
def _(sub_a: type[A], sub_b: type[B]):
reveal_type(A | sub_b) # revealed: <types.UnionType special-form>
reveal_type(sub_a | B) # revealed: <types.UnionType special-form>
reveal_type(sub_a | sub_b) # revealed: <types.UnionType special-form>
class C[T]: ...
class D[T]: ...
reveal_type(C | D) # revealed: <types.UnionType special-form 'C[Unknown] | D[Unknown]'>
reveal_type(C[int] | D[str]) # revealed: <types.UnionType special-form 'C[int] | D[str]'>

View File

@@ -5,7 +5,6 @@
```py
from typing import Literal
class Yes:
def __add__(self, other) -> Literal["+"]:
return "+"
@@ -46,13 +45,9 @@ class Yes:
def __floordiv__(self, other) -> Literal["//"]:
return "//"
class Sub(Yes): ...
class No: ...
# Yes implements all of the dunder methods.
reveal_type(Yes() + Yes()) # revealed: Literal["+"]
reveal_type(Yes() - Yes()) # revealed: Literal["-"]
@@ -145,7 +140,6 @@ reveal_type(No() // Yes()) # revealed: Unknown
```py
from typing import Literal
class Yes:
def __add__(self, other) -> Literal["+"]:
return "+"
@@ -186,7 +180,6 @@ class Yes:
def __floordiv__(self, other) -> Literal["//"]:
return "//"
class Sub(Yes):
def __radd__(self, other) -> Literal["r+"]:
return "r+"
@@ -227,7 +220,6 @@ class Sub(Yes):
def __rfloordiv__(self, other) -> Literal["r//"]:
return "r//"
class No:
def __radd__(self, other) -> Literal["r+"]:
return "r+"
@@ -268,7 +260,6 @@ class No:
def __rfloordiv__(self, other) -> Literal["r//"]:
return "r//"
# Subclass reflected dunder methods take precedence over the superclass's regular dunders.
reveal_type(Yes() + Sub()) # revealed: Literal["r+"]
reveal_type(Yes() - Sub()) # revealed: Literal["r-"]
@@ -311,18 +302,13 @@ class's type, i.e. `type`.)
```py
from typing import Literal
class Yes:
def __add__(self, other) -> Literal["+"]:
return "+"
class Sub(Yes): ...
class No: ...
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Yes'>`"
reveal_type(Yes + Yes) # revealed: Unknown
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `<class 'Sub'>`"
@@ -336,30 +322,22 @@ reveal_type(No + No) # revealed: Unknown
```py
from typing import Literal
class Yes:
def __add__(self, other) -> Literal["+"]:
return "+"
class Sub(Yes): ...
class No: ...
def yes() -> type[Yes]:
return Yes
def sub() -> type[Sub]:
return Sub
def no() -> type[No]:
return No
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `type[Yes]`"
reveal_type(yes() + yes()) # revealed: Unknown
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `type[Sub]`"
@@ -374,7 +352,6 @@ reveal_type(no() + no()) # revealed: Unknown
def f():
pass
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `def f() -> Unknown`"
reveal_type(f + f) # revealed: Unknown
# error: [unsupported-operator] "Operator `-` is not supported between two objects of type `def f() -> Unknown`"
@@ -421,10 +398,8 @@ class A: ...
```py
import mod1
class A: ...
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `mod2.A` and `mod1.A`"
A() + mod1.A()
```

View File

@@ -53,10 +53,8 @@ class A:
def __or__(self, other) -> "A":
return self
class B: ...
reveal_type(A() + B()) # revealed: A
reveal_type(A() - B()) # revealed: A
reveal_type(A() * B()) # revealed: A
@@ -117,10 +115,8 @@ class A:
def __ror__(self, other) -> "A":
return self
class B: ...
reveal_type(B() + A()) # revealed: A
reveal_type(B() - A()) # revealed: A
reveal_type(B() * A()) # revealed: A
@@ -148,10 +144,8 @@ class A:
def __rsub__(self, other) -> int:
return 1
class B: ...
reveal_type(A() + B()) # revealed: int
reveal_type(B() - A()) # revealed: int
```
@@ -166,15 +160,12 @@ class A:
def __add__(self, other: "B") -> int:
return 42
class B:
def __radd__(self, other: "A") -> str:
return "foo"
reveal_type(A() + B()) # revealed: int
# Edge case: C is a subtype of C, *but* if the two sides are of *equal* types,
# the lhs *still* takes precedence
class C:
@@ -184,7 +175,6 @@ class C:
def __radd__(self, other: "C") -> str:
return "foo"
reveal_type(C() + C()) # revealed: int
```
@@ -201,22 +191,17 @@ class A:
def __radd__(self, other) -> str:
return "foo"
class MyString(str): ...
class B(A):
def __radd__(self, other) -> MyString:
return MyString()
reveal_type(A() + B()) # revealed: MyString
# N.B. Still a subtype of `A`, even though `A` does not appear directly in the class's `__bases__`
class C(B): ...
reveal_type(A() + C()) # revealed: MyString
```
@@ -233,10 +218,8 @@ class A:
def __radd__(self, other) -> int:
return 42
class B(A): ...
reveal_type(A() + B()) # revealed: str
```
@@ -257,12 +240,10 @@ class A:
def __sub__(self, other: "A") -> "A":
return A()
class B:
def __rsub__(self, other: A) -> "B":
return B()
reveal_type(A() - B()) # revealed: B
```
@@ -275,11 +256,9 @@ class A:
def __call__(self, other) -> int:
return 42
class B:
__add__ = A()
reveal_type(B() + B()) # revealed: Unknown | int
```
@@ -290,7 +269,6 @@ the callable is declared:
class B2:
__add__: A = A()
reveal_type(B2() + B2()) # revealed: int
```
@@ -309,7 +287,6 @@ reveal_type(3.14 + 3j) # revealed: int | float | complex
reveal_type(42 + 4.2) # revealed: int | float
reveal_type(3 + 3j) # revealed: int | float | complex
def _(x: bool, y: int):
reveal_type(x + y) # revealed: int
reveal_type(4.2 + x) # revealed: int | float
@@ -329,7 +306,6 @@ class A:
def __radd__(self, other) -> "A":
return self
reveal_type(A() + 1) # revealed: A
reveal_type(1 + A()) # revealed: A
@@ -365,15 +341,12 @@ from does_not_exist import Foo # error: [unresolved-import]
reveal_type(Foo) # revealed: Unknown
class X:
def __add__(self, other: object) -> int:
return 42
class Y(Foo): ...
# TODO: Should be `int | Unknown`; see above discussion.
reveal_type(X() + Y()) # revealed: int
```
@@ -386,7 +359,6 @@ reveal_type(X() + Y()) # revealed: int
class NotBoolable:
__bool__: int = 3
a = NotBoolable()
# error: [unsupported-bool-conversion]
@@ -400,7 +372,6 @@ When operating on class objects, the corresponding dunder methods are looked up
```py
from __future__ import annotations
class Meta(type):
def __add__(self, other: Meta) -> int:
return 1
@@ -411,13 +382,9 @@ class Meta(type):
def __getitem__(self, key: int) -> str:
return "a"
class A(metaclass=Meta): ...
class B(metaclass=Meta): ...
reveal_type(A + B) # revealed: int
# error: [unsupported-operator] "Operator `-` is not supported between objects of type `<class 'A'>` and `<class 'B'>`"
reveal_type(A - B) # revealed: Unknown
@@ -441,12 +408,10 @@ The magic method must exist on the class, not just on the instance:
def add_impl(self, other) -> int:
return 1
class A:
def __init__(self):
self.__add__ = add_impl
# error: [unsupported-operator] "Operator `+` is not supported between two objects of type `A`"
# revealed: Unknown
reveal_type(A() + A())
@@ -457,7 +422,6 @@ reveal_type(A() + A())
```py
class A: ...
# error: [unsupported-operator]
# revealed: Unknown
reveal_type(A() + A())
@@ -472,15 +436,12 @@ class A:
def __add__(self, other) -> int:
return 1
class B:
def __radd__(self, other) -> int:
return 1
class C: ...
# error: [unsupported-operator]
# revealed: Unknown
reveal_type(C() + A())
@@ -502,7 +463,6 @@ class Foo:
def __radd__(self, other: "Foo") -> "Foo":
return self
# error: [unsupported-operator]
# revealed: Unknown
reveal_type(Foo() + Foo())

View File

@@ -16,7 +16,6 @@ reveal_type(7 ^ 2) # revealed: Literal[5]
# error: [unsupported-operator] "Operator `+` is not supported between objects of type `Literal[2]` and `Literal["f"]`"
reveal_type(2 + "f") # revealed: Unknown
def lhs(x: int):
reveal_type(x + 1) # revealed: int
reveal_type(x - 4) # revealed: int
@@ -25,7 +24,6 @@ def lhs(x: int):
reveal_type(x / 3) # revealed: int | float
reveal_type(x % 3) # revealed: int
def rhs(x: int):
reveal_type(2 + x) # revealed: int
reveal_type(3 - x) # revealed: int
@@ -34,7 +32,6 @@ def rhs(x: int):
reveal_type(-3 / x) # revealed: int | float
reveal_type(5 % x) # revealed: int
def both(x: int):
reveal_type(x + x) # revealed: int
reveal_type(x - x) # revealed: int
@@ -55,7 +52,6 @@ reveal_type(2**2) # revealed: Literal[4]
reveal_type(1 ** (largest_u32 + 1)) # revealed: int
reveal_type(2**largest_u32) # revealed: int
def variable(x: int):
reveal_type(x**2) # revealed: int
reveal_type(2**x) # revealed: Any
@@ -138,10 +134,8 @@ bool(1) / False
# error: "Cannot divide object of type `float` by zero"
reveal_type(1.0 / 0) # revealed: int | float
class MyInt(int): ...
# No error for a subclass of int
reveal_type(MyInt(3) / 0) # revealed: int | float
```

View File

@@ -8,7 +8,6 @@ reveal_type(() + (1, 2)) # revealed: tuple[Literal[1, 2], ...]
reveal_type((1, 2) + ()) # revealed: tuple[Literal[1, 2], ...]
reveal_type(() + ()) # revealed: tuple[()]
def _(x: tuple[int, str], y: tuple[None, tuple[int]]):
reveal_type(x + y) # revealed: tuple[int | str | None | tuple[int], ...]
reveal_type(y + x) # revealed: tuple[None | tuple[int] | int | str, ...]
@@ -39,7 +38,6 @@ ThreeFour = tuple[Literal[3], Literal[4]]
IntTuple = tuple[int, ...]
StrTuple = tuple[str, ...]
def _(one_two: OneTwo, x: IntTuple, y: StrTuple, three_four: ThreeFour):
reveal_type(x + x) # revealed: tuple[int, ...]
reveal_type(x + y) # revealed: tuple[int | str, ...]

View File

@@ -28,7 +28,6 @@ the possible outcomes:
```py
from typing import Literal
def f3(two_or_three: Literal[2, 3], a_or_b: Literal["a", "b"]):
reveal_type(two_or_three + two_or_three) # revealed: Literal[4, 5, 6]
reveal_type(two_or_three**two_or_three) # revealed: Literal[4, 8, 9, 27]

View File

@@ -39,17 +39,14 @@ If a symbol has a declared type (`int`), we use that even if there is a more pre
```py
from typing import Any
def any() -> Any: ...
class Public:
a: int = 1
b: str = 2 # error: [invalid-assignment]
c: Any = 3
d: int = any()
reveal_type(Public.a) # revealed: int
reveal_type(Public.b) # revealed: str
reveal_type(Public.c) # revealed: Any
@@ -63,12 +60,10 @@ If a symbol is declared and *possibly* unbound, we trust the declared type witho
```py
from typing import Any
def any() -> Any: ...
def flag() -> bool:
return True
class Public:
a: int
b: str
@@ -81,7 +76,6 @@ class Public:
c = 3
d = any()
reveal_type(Public.a) # revealed: int
reveal_type(Public.b) # revealed: str
reveal_type(Public.c) # revealed: Any
@@ -96,12 +90,10 @@ is available somehow and simply use the declared type.
```py
from typing import Any
class Public:
a: int
b: Any
reveal_type(Public.a) # revealed: int
reveal_type(Public.b) # revealed: Any
```
@@ -116,12 +108,10 @@ inferred types:
```py
from typing import Any
def any() -> Any: ...
def flag() -> bool:
return True
class Public:
a = 1
b = 2
@@ -133,7 +123,6 @@ class Public:
c: str # error: [invalid-declaration]
d: int
reveal_type(Public.a) # revealed: int
reveal_type(Public.b) # revealed: Literal[2] | Any
reveal_type(Public.c) # revealed: Literal[3] | Unknown
@@ -154,11 +143,9 @@ error for both `a` and `b`:
```py
from typing import Any
def flag() -> bool:
return True
class Public:
if flag():
a: Any = 1
@@ -166,7 +153,6 @@ class Public:
else:
b: str
# error: [possibly-missing-attribute]
reveal_type(Public.a) # revealed: Literal[1] | Any
# error: [possibly-missing-attribute]
@@ -187,12 +173,10 @@ seems inconsistent when compared to the case just above.
def flag() -> bool:
return True
class Public:
if flag():
a: int
# TODO: this should raise an error. Once we fix this, update the section description and the table
# on top of this document.
reveal_type(Public.a) # revealed: int
@@ -218,7 +202,6 @@ class Public:
# Implicitly declared with `Unknown`, due to the usage of an unknown name in the annotation:
b: SomeUnknownName = 1 # error: [unresolved-reference]
reveal_type(Public.a) # revealed: Unknown | Literal[1]
reveal_type(Public.b) # revealed: Unknown
@@ -235,13 +218,11 @@ inconsistent when compared to the "possibly-undeclared-and-possibly-unbound" cas
def flag() -> bool:
return True
class Public:
if flag:
a = 1
b: SomeUnknownName = 1 # error: [unresolved-reference]
# TODO: these should raise an error. Once we fix this, update the section description and the table
# on top of this document.
reveal_type(Public.a) # revealed: Unknown | Literal[1]
@@ -260,7 +241,6 @@ class Public:
if False:
a: int = 1
# error: [unresolved-attribute]
reveal_type(Public.a) # revealed: Unknown

View File

@@ -3,11 +3,9 @@
```py
from typing import Callable
def _(c: Callable[[], int]):
reveal_type(c()) # revealed: int
def _(c: Callable[[int, str], int]):
reveal_type(c(1, "a")) # revealed: int

View File

@@ -6,7 +6,6 @@
class NotBool:
__bool__ = None
# error: [too-many-positional-arguments] "Too many positional arguments to class `bool`: expected 1, got 2"
bool(1, 2)
@@ -69,12 +68,10 @@ from enum import Enum
from types import FunctionType
from typing import TypeVar
class Answer(Enum):
NO = 0
YES = 1
reveal_type(isinstance(True, bool)) # revealed: Literal[True]
reveal_type(isinstance(True, int)) # revealed: Literal[True]
reveal_type(isinstance(True, object)) # revealed: Literal[True]
@@ -85,27 +82,17 @@ reveal_type(isinstance(Answer.NO, Answer)) # revealed: Literal[True]
reveal_type(isinstance((1, 2), tuple)) # revealed: Literal[True]
def f(): ...
reveal_type(isinstance(f, FunctionType)) # revealed: Literal[True]
reveal_type(isinstance("", int)) # revealed: bool
class A: ...
class SubclassOfA(A): ...
class OtherSubclassOfA(A): ...
class B: ...
reveal_type(isinstance(A, type)) # revealed: Literal[True]
a = A()
@@ -119,7 +106,6 @@ s = SubclassOfA()
reveal_type(isinstance(s, SubclassOfA)) # revealed: Literal[True]
reveal_type(isinstance(s, A)) # revealed: Literal[True]
def _(x: A | B, y: list[int]):
reveal_type(isinstance(y, list)) # revealed: Literal[True]
reveal_type(isinstance(x, A)) # revealed: bool
@@ -130,12 +116,10 @@ def _(x: A | B, y: list[int]):
reveal_type(x) # revealed: B & ~A
reveal_type(isinstance(x, B)) # revealed: Literal[True]
T = TypeVar("T")
T_bound_A = TypeVar("T_bound_A", bound=A)
T_constrained = TypeVar("T_constrained", SubclassOfA, OtherSubclassOfA)
def _(
x: T,
x_bound_a: T_bound_A,

View File

@@ -10,14 +10,11 @@ class Multiplier:
def __call__(self, number: int) -> int:
return number * self.factor
a = Multiplier(2)(3)
reveal_type(a) # revealed: int
class Unit: ...
b = Unit()(3.0) # error: "Object of type `Unit` is not callable"
reveal_type(b) # revealed: Unknown
```
@@ -28,7 +25,6 @@ reveal_type(b) # revealed: Unknown
def _(flag: bool):
class PossiblyNotCallable:
if flag:
def __call__(self) -> int:
return 1
@@ -42,7 +38,6 @@ def _(flag: bool):
```py
def _(flag: bool):
if flag:
class PossiblyUnbound:
def __call__(self) -> int:
return 1
@@ -58,7 +53,6 @@ def _(flag: bool):
class NonCallable:
__call__ = 1
a = NonCallable()
# error: [call-non-callable] "Object of type `Literal[1]` is not callable"
reveal_type(a()) # revealed: Unknown
@@ -72,7 +66,6 @@ def _(flag: bool):
if flag:
__call__ = 1
else:
def __call__(self) -> int:
return 1
@@ -90,7 +83,6 @@ class C:
def __call__(self, x: int) -> int:
return 1
c = C()
# error: 15 [invalid-argument-type] "Argument to bound method `__call__` is incorrect: Expected `int`, found `Literal["foo"]`"
@@ -105,7 +97,6 @@ class C:
def __call__(self: int) -> int:
return 1
c = C()
# error: 13 [invalid-argument-type] "Argument to bound method `__call__` is incorrect: Expected `int`, found `C`"
@@ -120,7 +111,6 @@ reveal_type(c()) # revealed: int
def outer(cond1: bool):
class Test:
if cond1:
def __call__(self): ...
class Other:

View File

@@ -16,12 +16,10 @@ the first argument:
from ty_extensions import CallableTypeOf
from typing import Callable
class C1:
def method(self: C1, x: int) -> str:
return str(x)
def _(
accessed_on_class: CallableTypeOf[C1.method],
accessed_on_instance: CallableTypeOf[C1().method],
@@ -39,11 +37,9 @@ class NonDescriptorCallable2:
def __call__(self, c2: C2, x: int) -> str:
return str(x)
class C2:
non_descriptor_callable: NonDescriptorCallable2 = NonDescriptorCallable2()
def _(
accessed_on_class: CallableTypeOf[C2.non_descriptor_callable],
accessed_on_instance: CallableTypeOf[C2().non_descriptor_callable],
@@ -59,11 +55,9 @@ class NonDescriptorCallable3:
def __call__(self, c3: C3, x: int) -> str:
return str(x)
class C3:
def method(self: C3, x: int) -> str:
return str(x)
non_descriptor_callable: NonDescriptorCallable3 = NonDescriptorCallable3()
callable_m: Callable[[C3, int], str] = method
@@ -119,11 +113,9 @@ intention that it shouldn't influence the method's descriptor behavior. For exam
```py
from typing import Callable
def memoize[**P, R](f: Callable[P, R]) -> Callable[P, R]:
raise NotImplementedError
class C1:
def method(self, x: int) -> str:
return str(x)
@@ -132,7 +124,6 @@ class C1:
def method_decorated(self, x: int) -> str:
return str(x)
C1().method(1)
C1().method_decorated(1)
@@ -144,13 +135,11 @@ This also works with an argumentless `Callable` annotation:
def memoize2(f: Callable) -> Callable:
raise NotImplementedError
class C2:
@memoize2
def method_decorated(self, x: int) -> str:
return str(x)
C2().method_decorated(1)
```
@@ -159,17 +148,14 @@ And with unions of `Callable` types:
```py
from typing import Callable
def expand(f: Callable[[C3, int], int]) -> Callable[[C3, int], int] | Callable[[C3, int], str]:
raise NotImplementedError
class C3:
@expand
def method_decorated(self, x: int) -> int:
return x
reveal_type(C3().method_decorated(1)) # revealed: int | str
```
@@ -181,14 +167,11 @@ but here we emit errors:
def memoize3(f: Callable[[C4, int], str]) -> Callable[[C4, int], str]:
raise NotImplementedError
class C4:
def method(self, x: int) -> str:
return str(x)
method_decorated = memoize3(method)
# error: [missing-argument]
# error: [invalid-argument-type]
C4().method_decorated(1)
@@ -211,15 +194,12 @@ class SquareCalculator:
def __call__(self, x: float) -> int:
return self.post_process(x * x)
def square_then(c: Callable[[float], int]) -> Callable[[float], int]:
return SquareCalculator(c)
class Calculator:
square_then_round = square_then(round)
reveal_type(Calculator().square_then_round(3.14)) # revealed: Unknown | int
```
@@ -232,15 +212,12 @@ example. We generally treat dunder attributes as bound-method descriptors since
```py
from typing import Callable
def pow_impl(tensor: Tensor, exponent: int) -> Tensor:
raise NotImplementedError
class Tensor:
__pow__: Callable[[Tensor, int], Tensor] = pow_impl
Tensor() ** 2
```
@@ -252,11 +229,9 @@ treat it as a bound-method descriptor:
def make_comparison_operator(name: str) -> Callable[[Matrix, Matrix], bool]:
raise NotImplementedError
class Matrix:
__lt__ = make_comparison_operator("lt")
Matrix() < Matrix()
```
@@ -270,17 +245,14 @@ function-like:
```py
from typing import Callable
def my_lossy_decorator(fn: Callable[..., int]) -> Callable[..., int]:
return fn
class MyClass:
@my_lossy_decorator
def method(self) -> int:
return 42
reveal_type(MyClass().method) # revealed: (...) -> int
reveal_type(MyClass().method.__name__) # revealed: str
```
@@ -295,11 +267,9 @@ behavior.
```py
from typing import Callable
def callable_identity[**P, R](func: Callable[P, R]) -> Callable[P, R]:
return func
class C:
@callable_identity
@classmethod
@@ -311,7 +281,6 @@ class C:
def f2(cls, x: int) -> str:
return "a"
# error: [too-many-positional-arguments]
# error: [invalid-argument-type]
C.f1(C, 1)
@@ -334,22 +303,18 @@ The callable type of a type object is not function-like.
from typing import ClassVar
from ty_extensions import CallableTypeOf
class WithNew:
def __new__(self, x: int) -> WithNew:
return super().__new__(WithNew)
class WithInit:
def __init__(self, x: int) -> None:
pass
class C:
with_new: ClassVar[CallableTypeOf[WithNew]]
with_init: ClassVar[CallableTypeOf[WithInit]]
C.with_new(1)
C().with_new(1)
C.with_init(1)

View File

@@ -45,7 +45,6 @@ reveal_type(object(1)) # revealed: object
```py
class Foo: ...
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
@@ -59,7 +58,6 @@ class Foo:
def __new__(cls, x: int) -> "Foo":
return object.__new__(cls)
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
@@ -76,15 +74,12 @@ constructor from it.
```py
from typing_extensions import Self
class Base:
def __new__(cls, x: int) -> Self:
return cls()
class Foo(Base): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
@@ -99,11 +94,8 @@ reveal_type(Foo(1, 2)) # revealed: Foo
def _(flag: bool) -> None:
class Foo:
if flag:
def __new__(cls, x: int): ...
else:
def __new__(cls, x: int, y: int = 1): ...
reveal_type(Foo(1)) # revealed: Foo
@@ -126,16 +118,13 @@ class SomeCallable:
obj.x = x
return obj
class Descriptor:
def __get__(self, instance, owner) -> SomeCallable:
return SomeCallable()
class Foo:
__new__: Descriptor = Descriptor()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
@@ -150,11 +139,9 @@ class Callable:
def __call__(self, cls, x: int) -> "Foo":
return object.__new__(cls)
class Foo:
__new__ = Callable()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
@@ -168,7 +155,6 @@ reveal_type(Foo()) # revealed: Foo
def _(flag: bool) -> None:
class Foo:
if flag:
def __new__(cls):
return object.__new__(cls)
@@ -186,7 +172,6 @@ def _(flag: bool) -> None:
def _(flag: bool) -> None:
class Callable:
if flag:
def __call__(self, cls, x: int) -> "Foo":
return object.__new__(cls)
@@ -209,7 +194,6 @@ If the class has an `__init__` method, we can infer the signature of the constru
class Foo:
def __init__(self, x: int): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
@@ -227,10 +211,8 @@ constructor from it.
class Base:
def __init__(self, x: int): ...
class Foo(Base): ...
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
@@ -245,11 +227,8 @@ reveal_type(Foo(1, 2)) # revealed: Foo
def _(flag: bool) -> None:
class Foo:
if flag:
def __init__(self, x: int): ...
else:
def __init__(self, x: int, y: int = 1): ...
reveal_type(Foo(1)) # revealed: Foo
@@ -274,16 +253,13 @@ class SomeCallable:
def __call__(self, x: int) -> str:
return "a"
class Descriptor:
def __get__(self, instance, owner) -> SomeCallable:
return SomeCallable()
class Foo:
__init__: Descriptor = Descriptor()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
@@ -298,11 +274,9 @@ class Callable:
def __call__(self, x: int) -> None:
pass
class Foo:
__init__ = Callable()
reveal_type(Foo(1)) # revealed: Foo
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__call__`"
reveal_type(Foo()) # revealed: Foo
@@ -314,7 +288,6 @@ reveal_type(Foo()) # revealed: Foo
def _(flag: bool) -> None:
class Callable:
if flag:
def __call__(self, x: int) -> None:
pass
@@ -347,7 +320,6 @@ class Foo:
def __init__(self, x: int): ...
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
@@ -368,7 +340,6 @@ class Foo:
def __init__(self, x: int) -> None:
self.x = x
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
reveal_type(Foo(1)) # revealed: Foo
@@ -382,7 +353,6 @@ reveal_type(Foo(1, 2)) # revealed: Foo
```py
import abc
class Foo:
def __new__(cls) -> "Foo":
return object.__new__(cls)
@@ -390,14 +360,12 @@ class Foo:
def __init__(self, x):
self.x = 42
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo()) # revealed: Foo
# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2"
reveal_type(Foo(42)) # revealed: Foo
class Foo2:
def __new__(cls, x) -> "Foo2":
return object.__new__(cls)
@@ -405,14 +373,12 @@ class Foo2:
def __init__(self):
pass
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
reveal_type(Foo2()) # revealed: Foo2
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
reveal_type(Foo2(42)) # revealed: Foo2
class Foo3(metaclass=abc.ABCMeta):
def __new__(cls) -> "Foo3":
return object.__new__(cls)
@@ -420,14 +386,12 @@ class Foo3(metaclass=abc.ABCMeta):
def __init__(self, x):
self.x = 42
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
reveal_type(Foo3()) # revealed: Foo3
# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2"
reveal_type(Foo3(42)) # revealed: Foo3
class Foo4(metaclass=abc.ABCMeta):
def __new__(cls, x) -> "Foo4":
return object.__new__(cls)
@@ -435,7 +399,6 @@ class Foo4(metaclass=abc.ABCMeta):
def __init__(self):
pass
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
reveal_type(Foo4()) # revealed: Foo4
@@ -452,7 +415,6 @@ meta-type, never on the type itself).
```py
from typing_extensions import Literal
class Meta(type):
def __new__(mcls, name, bases, namespace, /, **kwargs):
return super().__new__(mcls, name, bases, namespace)
@@ -460,10 +422,8 @@ class Meta(type):
def __lt__(cls, other) -> Literal[True]:
return True
class C(metaclass=Meta): ...
# No error is raised here, since we don't implicitly call `Meta.__new__`
reveal_type(C()) # revealed: C

View File

@@ -16,12 +16,10 @@ as the `instance` argument to `__get__`. A desugared version of `obj[key]` is ro
```py
from typing import Any
def find_name_in_mro(typ: type, name: str) -> Any:
# See implementation in https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
pass
def getitem_desugared(obj: object, key: object) -> object:
getitem_callable = find_name_in_mro(type(obj), "__getitem__")
if hasattr(getitem_callable, "__get__"):
@@ -42,11 +40,9 @@ class Meta(type):
def __getitem__(cls, key: int) -> str:
return str(key)
class DunderOnMetaclass(metaclass=Meta):
pass
reveal_type(DunderOnMetaclass[0]) # revealed: str
```
@@ -57,7 +53,6 @@ class ClassWithNormalDunder:
def __getitem__(self, key: int) -> str:
return str(key)
# error: [not-subscriptable]
ClassWithNormalDunder[0]
```
@@ -73,7 +68,6 @@ class ClassWithNormalDunder:
def __getitem__(self, key: int) -> str:
return str(key)
class_with_normal_dunder = ClassWithNormalDunder()
reveal_type(class_with_normal_dunder[0]) # revealed: str
@@ -85,12 +79,10 @@ Which can be demonstrated by trying to attach a dunder method to an instance, wh
def external_getitem(instance, key: int) -> str:
return str(key)
class ThisFails:
def __init__(self):
self.__getitem__ = external_getitem
this_fails = ThisFails()
# error: [not-subscriptable] "Cannot subscript object of type `ThisFails` with no `__getitem__` method"
@@ -109,11 +101,9 @@ The instance-level method is also not called when the class-level method is pres
def external_getitem1(instance, key) -> str:
return "a"
def external_getitem2(key) -> int:
return 1
def _(flag: bool):
class ThisFails:
if flag:
@@ -139,11 +129,9 @@ Class-level annotations with no value assigned are considered to be accessible o
```py
from typing import Callable
class C:
__call__: Callable[..., None]
C()()
_: Callable[..., None] = C()
@@ -154,12 +142,10 @@ And of course the same is true if we have only an implicit assignment inside a m
```py
from typing import Callable
class C:
def __init__(self):
self.__call__ = lambda *a, **kw: None
# error: [call-non-callable]
C()()
@@ -176,11 +162,9 @@ class SomeCallable:
def __call__(self, key: int) -> str:
return str(key)
class ClassWithNonMethodDunder:
__getitem__: SomeCallable = SomeCallable()
class_with_callable_dunder = ClassWithNonMethodDunder()
reveal_type(class_with_callable_dunder[0]) # revealed: str
@@ -194,21 +178,17 @@ that the `instance` argument is on object of type `ClassWithDescriptorDunder`:
```py
from __future__ import annotations
class SomeCallable:
def __call__(self, key: int) -> str:
return str(key)
class Descriptor:
def __get__(self, instance: ClassWithDescriptorDunder, owner: type[ClassWithDescriptorDunder]) -> SomeCallable:
return SomeCallable()
class ClassWithDescriptorDunder:
__getitem__: Descriptor = Descriptor()
class_with_descriptor_dunder = ClassWithDescriptorDunder()
reveal_type(class_with_descriptor_dunder[0]) # revealed: str
@@ -228,7 +208,6 @@ class C:
# error: [invalid-assignment]
self.__getitem__ = None
# This is still fine, and simply calls the `__getitem__` method on the class
reveal_type(C()[0]) # revealed: str
```
@@ -239,12 +218,9 @@ reveal_type(C()[0]) # revealed: str
def _(flag: bool):
class C:
if flag:
def __getitem__(self, key: int) -> str:
return str(key)
else:
def __getitem__(self, key: int) -> bytes:
return bytes()
@@ -252,13 +228,11 @@ def _(flag: bool):
reveal_type(c[0]) # revealed: str | bytes
if flag:
class D:
def __getitem__(self, key: int) -> str:
return str(key)
else:
class D:
def __getitem__(self, key: int) -> bytes:
return bytes()
@@ -276,17 +250,14 @@ regular method calls.
def external_getitem(instance, key: int) -> str:
return str(key)
class NotSubscriptable1:
def __init__(self, value: int):
self.__getitem__ = external_getitem
class NotSubscriptable2:
def __init__(self, value: int):
self.__getitem__ = external_getitem
def _(union: NotSubscriptable1 | NotSubscriptable2):
# error: [not-subscriptable] "Cannot subscript object of type `NotSubscriptable2` with no `__getitem__` method"
# error: [not-subscriptable] "Cannot subscript object of type `NotSubscriptable1` with no `__getitem__` method"
@@ -299,7 +270,6 @@ def _(union: NotSubscriptable1 | NotSubscriptable2):
def _(flag: bool):
class C:
if flag:
def __getitem__(self, key: int) -> str:
return str(key)

View File

@@ -6,7 +6,6 @@
def get_int() -> int:
return 42
reveal_type(get_int()) # revealed: int
```
@@ -16,7 +15,6 @@ reveal_type(get_int()) # revealed: int
async def get_int_async() -> int:
return 42
reveal_type(get_int_async()) # revealed: CoroutineType[Any, Any, int]
```
@@ -31,7 +29,6 @@ python-version = "3.12"
def get_int[T]() -> int:
return 42
reveal_type(get_int()) # revealed: int
```
@@ -40,20 +37,16 @@ reveal_type(get_int()) # revealed: int
```py
from typing import Callable
def foo() -> int:
return 42
def decorator(func) -> Callable[[], int]:
return foo
@decorator
def bar() -> str:
return "bar"
reveal_type(bar()) # revealed: int
```
@@ -69,10 +62,8 @@ x = nonsense() # error: "Object of type `Literal[123]` is not callable"
```py
def _(flag: bool):
if flag:
def foo() -> int:
return 42
# error: [possibly-unresolved-reference]
reveal_type(foo()) # revealed: int
```
@@ -98,7 +89,6 @@ still continue to use the old convention, so it is supported by ty as well.
```py
def f(__x: int): ...
f(1)
# error: [positional-only-parameter-as-kwarg]
f(__x=1)
@@ -109,7 +99,6 @@ But not if they follow a non-positional-only parameter:
```py
def g(x: int, __y: str): ...
g(x=1, __y="foo")
```
@@ -118,7 +107,6 @@ And also not if they both start and end with `__`:
```py
def h(__x__: str): ...
h(__x__="foo")
```
@@ -127,7 +115,6 @@ And if *any* parameters use the new PEP-570 convention, the old convention does
```py
def i(x: str, /, __y: int): ...
i("foo", __y=42) # fine
```
@@ -138,13 +125,11 @@ class C:
def method(self, __x: int): ...
@classmethod
def class_method(cls, __x: str): ...
# (the name of the first parameter is irrelevant;
# a staticmethod works the same as a free function in the global scope)
@staticmethod
def static_method(self, __x: int): ...
# error: [positional-only-parameter-as-kwarg]
C().method(__x=1)
# error: [positional-only-parameter-as-kwarg]
@@ -168,10 +153,8 @@ def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...
# Test all of the above with a number of different splatted argument types
def _(args: list[int]) -> None:
takes_zero(*args)
takes_one(*args)
@@ -186,7 +169,6 @@ def _(args: list[int]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: tuple[int, ...]) -> None:
takes_zero(*args)
takes_one(*args)
@@ -214,10 +196,8 @@ def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...
# Test all of the above with a number of different splatted argument types
def _(args: tuple[int]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args)
@@ -230,7 +210,6 @@ def _(args: tuple[int]) -> None:
takes_at_least_two(*args) # error: [missing-argument]
takes_at_least_two_positional_only(*args) # error: [missing-argument]
def _(args: tuple[int, int]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -243,7 +222,6 @@ def _(args: tuple[int, int]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: tuple[int, str]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -271,13 +249,10 @@ def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...
# Test all of the above with a number of different splatted argument types
class SingleElementTuple(tuple[int]): ...
def _(args: SingleElementTuple) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
@@ -295,10 +270,8 @@ def _(args: SingleElementTuple) -> None:
takes_at_least_two(*args) # error: [missing-argument]
takes_at_least_two_positional_only(*args) # error: [missing-argument]
class TwoElementIntTuple(tuple[int, int]): ...
def _(args: TwoElementIntTuple) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -311,10 +284,8 @@ def _(args: TwoElementIntTuple) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
class IntStrTuple(tuple[int, str]): ...
def _(args: IntStrTuple) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
@@ -355,10 +326,8 @@ def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...
# Test all of the above with a number of different splatted argument types
def _(args: tuple[int, *tuple[int, ...]]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args)
@@ -371,7 +340,6 @@ def _(args: tuple[int, *tuple[int, ...]]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: tuple[int, *tuple[str, ...]]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args)
@@ -384,7 +352,6 @@ def _(args: tuple[int, *tuple[str, ...]]) -> None:
takes_at_least_two(*args) # error: [invalid-argument-type]
takes_at_least_two_positional_only(*args) # error: [invalid-argument-type]
def _(args: tuple[int, int, *tuple[int, ...]]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -397,7 +364,6 @@ def _(args: tuple[int, int, *tuple[int, ...]]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: tuple[int, int, *tuple[str, ...]]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -410,7 +376,6 @@ def _(args: tuple[int, int, *tuple[str, ...]]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: tuple[int, *tuple[int, ...], int]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -423,7 +388,6 @@ def _(args: tuple[int, *tuple[int, ...], int]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: tuple[int, *tuple[str, ...], int]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -456,13 +420,10 @@ def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...
# Test all of the above with a number of different splatted argument types
class IntStarInt(tuple[int, *tuple[int, ...]]): ...
def _(args: IntStarInt) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args)
@@ -475,10 +436,8 @@ def _(args: IntStarInt) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
class IntStarStr(tuple[int, *tuple[str, ...]]): ...
def _(args: IntStarStr) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
@@ -501,10 +460,8 @@ def _(args: IntStarStr) -> None:
# error: [invalid-argument-type]
takes_at_least_two_positional_only(*args)
class IntIntStarInt(tuple[int, int, *tuple[int, ...]]): ...
def _(args: IntIntStarInt) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -517,10 +474,8 @@ def _(args: IntIntStarInt) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
class IntIntStarStr(tuple[int, int, *tuple[str, ...]]): ...
def _(args: IntIntStarStr) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
@@ -542,10 +497,8 @@ def _(args: IntIntStarStr) -> None:
takes_at_least_two_positional_only(*args)
class IntStarIntInt(tuple[int, *tuple[int, ...], int]): ...
def _(args: IntStarIntInt) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -558,10 +511,8 @@ def _(args: IntStarIntInt) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
class IntStarStrInt(tuple[int, *tuple[str, ...], int]): ...
def _(args: IntStarStrInt) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
@@ -591,7 +542,6 @@ def _(args: IntStarStrInt) -> None:
```py
from typing import Literal
def takes_zero() -> None: ...
def takes_one(x: str) -> None: ...
def takes_two(x: str, y: str) -> None: ...
@@ -603,10 +553,8 @@ def takes_at_least_one(x: str, *args) -> None: ...
def takes_at_least_two(x: str, y: str, *args) -> None: ...
def takes_at_least_two_positional_only(x: str, y: str, /, *args) -> None: ...
# Test all of the above with a number of different splatted argument types
def _(args: Literal["a"]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args)
@@ -623,7 +571,6 @@ def _(args: Literal["a"]) -> None:
takes_at_least_two(*args) # error: [missing-argument]
takes_at_least_two_positional_only(*args) # error: [missing-argument]
def _(args: Literal["ab"]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -636,7 +583,6 @@ def _(args: Literal["ab"]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: Literal["abc"]) -> None:
takes_zero(*args) # error: [too-many-positional-arguments]
takes_one(*args) # error: [too-many-positional-arguments]
@@ -653,7 +599,6 @@ def _(args: Literal["abc"]) -> None:
takes_at_least_two(*args)
takes_at_least_two_positional_only(*args)
def _(args: str) -> None:
takes_zero(*args)
takes_one(*args)
@@ -675,7 +620,6 @@ def _(args: str) -> None:
def f(x: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo")) # revealed: int
```
@@ -686,7 +630,6 @@ reveal_type(f("foo")) # revealed: int
def f(x: int, /) -> int:
return 1
# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo")) # revealed: int
```
@@ -697,7 +640,6 @@ reveal_type(f("foo")) # revealed: int
def f(*args: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo")) # revealed: int
```
@@ -713,7 +655,6 @@ python-version = "3.11"
def f(*args: int) -> int:
return 1
def _(args: list[str]) -> None:
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `str`"
reveal_type(f(*args)) # revealed: int
@@ -760,7 +701,6 @@ A union of heterogeneous tuples provided to a variadic parameter:
# - <https://github.com/home-assistant/core/blob/bde4eb50111a72f9717fe73ee5929e50eb06911b/homeassistant/components/lovelace/websocket.py#L50-L59>
# - <https://github.com/pydata/xarray/blob/3572f4e70f2b12ef9935c1f8c3c1b74045d2a092/xarray/tests/test_groupby.py#L3058-L3059>
def f2(a: str, b: bool): ...
def f3(coinflip: bool):
if coinflip:
@@ -782,10 +722,8 @@ def f3(coinflip: bool):
# error: [invalid-argument-type] "Argument to function `f2` is incorrect: Expected `bool`, found `Literal[True] | tuple[Literal[True]]`"
f2(*other_args)
def f4(a=None, b=None, c=None, d=None, e=None): ...
my_args = ((1, 2), (3, 4), (5, 6))
for tup in my_args:
@@ -812,7 +750,6 @@ python-version = "3.11"
def f(x: int, *args: str) -> int:
return 1
def _(
args1: list[int],
args2: tuple[int],
@@ -848,7 +785,6 @@ def _(
def f(x: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo")) # revealed: int
```
@@ -859,7 +795,6 @@ reveal_type(f(x="foo")) # revealed: int
def f(*, x: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo")) # revealed: int
```
@@ -870,7 +805,6 @@ reveal_type(f(x="foo")) # revealed: int
def f(**kwargs: int) -> int:
return 1
# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo")) # revealed: int
```
@@ -881,7 +815,6 @@ reveal_type(f(x="foo")) # revealed: int
def f(x: int = 1, y: str = "foo") -> int:
return 1
# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `Literal[2]`"
# error: 20 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["bar"]`"
reveal_type(f(y=2, x="bar")) # revealed: int
@@ -894,16 +827,10 @@ reveal_type(f(y=2, x="bar")) # revealed: int
```py
from typing import Sized
class Foo: ...
class Bar: ...
class Baz: ...
def f(x: Sized): ...
def g(
a: str | Foo,
@@ -925,7 +852,6 @@ def g(
def f() -> int:
return 1
# error: 15 [too-many-positional-arguments] "Too many positional arguments to function `f`: expected 0, got 1"
reveal_type(f("foo")) # revealed: int
```
@@ -936,7 +862,6 @@ reveal_type(f("foo")) # revealed: int
def f() -> int:
return 1
# error: 15 [too-many-positional-arguments] "Too many positional arguments to function `f`: expected 0, got 2"
reveal_type(f("foo", "bar")) # revealed: int
```
@@ -947,7 +872,6 @@ reveal_type(f("foo", "bar")) # revealed: int
def f(*args: int) -> int:
return 1
reveal_type(f(1, 2, 3)) # revealed: int
```
@@ -957,7 +881,6 @@ reveal_type(f(1, 2, 3)) # revealed: int
def f(**kwargs: int) -> int:
return 1
reveal_type(f(foo=1, bar=2)) # revealed: int
```
@@ -969,7 +892,6 @@ reveal_type(f(foo=1, bar=2)) # revealed: int
def f(x: int) -> int:
return 1
# error: 13 [missing-argument] "No argument provided for required parameter `x` of function `f`"
reveal_type(f()) # revealed: int
```
@@ -980,7 +902,6 @@ reveal_type(f()) # revealed: int
def f(x: int, y: str = "foo") -> int:
return 1
# error: 13 [missing-argument] "No argument provided for required parameter `x` of function `f`"
reveal_type(f()) # revealed: int
```
@@ -991,7 +912,6 @@ reveal_type(f()) # revealed: int
def f(x: int = 1) -> int:
return 1
reveal_type(f()) # revealed: int
```
@@ -1001,7 +921,6 @@ reveal_type(f()) # revealed: int
def f(x: int, *y: str) -> int:
return 1
# error: 13 [missing-argument] "No argument provided for required parameter `x` of function `f`"
reveal_type(f()) # revealed: int
```
@@ -1012,7 +931,6 @@ reveal_type(f()) # revealed: int
def f(*args: int) -> int:
return 1
reveal_type(f()) # revealed: int
```
@@ -1022,7 +940,6 @@ reveal_type(f()) # revealed: int
def f(**kwargs: int) -> int:
return 1
reveal_type(f()) # revealed: int
```
@@ -1032,7 +949,6 @@ reveal_type(f()) # revealed: int
def f(x: int, y: int) -> int:
return 1
# error: 13 [missing-argument] "No arguments provided for required parameters `x`, `y` of function `f`"
reveal_type(f()) # revealed: int
```
@@ -1043,7 +959,6 @@ reveal_type(f()) # revealed: int
def f(x: int) -> int:
return 1
# error: 20 [unknown-argument] "Argument `y` does not match any known parameter of function `f`"
reveal_type(f(x=1, y=2)) # revealed: int
```
@@ -1054,7 +969,6 @@ reveal_type(f(x=1, y=2)) # revealed: int
def f(x: int) -> int:
return 1
# error: 18 [parameter-already-assigned] "Multiple values provided for parameter `x` of function `f`"
reveal_type(f(1, x=2)) # revealed: int
```
@@ -1130,7 +1044,6 @@ def empty() -> None: ...
def _(kwargs: dict[str, int]) -> None:
empty(**kwargs)
empty(**{})
empty(**dict())
```
@@ -1140,19 +1053,15 @@ empty(**dict())
```py
from typing_extensions import TypedDict
def f(**kwargs: int) -> None: ...
class Foo(TypedDict):
a: int
b: int
def _(kwargs: dict[str, int]) -> None:
f(**kwargs)
f(**{"foo": 1})
f(**dict(foo=1))
f(**Foo(a=1, b=2))
@@ -1176,17 +1085,14 @@ def _(kwargs: dict[str, int]) -> None:
```py
from typing_extensions import TypedDict
class Foo(TypedDict):
a: int
b: int
def f(a: int, b: int) -> None: ...
def _(kwargs: dict[str, int]) -> None:
f(**kwargs)
f(**{"a": 1, "b": 2})
f(**dict(a=1, b=2))
f(**Foo(a=1, b=2))
@@ -1197,17 +1103,14 @@ f(**Foo(a=1, b=2))
```py
from typing_extensions import TypedDict
class Foo(TypedDict):
a: int
b: int
def f(*, a: int, b: int) -> None: ...
def _(kwargs: dict[str, int]) -> None:
f(**kwargs)
f(**{"a": 1, "b": 2})
f(**dict(a=1, b=2))
f(**Foo(a=1, b=2))
@@ -1232,7 +1135,6 @@ def _(kwargs1: dict[str, int], kwargs2: dict[str, int], kwargs3: dict[str, str],
```py
class B: ...
def f(*, a: int, b: B, **kwargs: int) -> None: ...
def _(kwargs: dict[str, int]):
# Make sure that the `b` argument is not being matched against `kwargs` by passing an integer
@@ -1258,20 +1160,16 @@ def _(kwargs1: dict[str, int], kwargs2: dict[str, str]):
```py
from typing_extensions import NotRequired, TypedDict
class Foo1(TypedDict):
a: int
b: str
class Foo2(TypedDict):
a: int
b: NotRequired[str]
def f(**kwargs: int) -> None: ...
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `str`"
f(**Foo1(a=1, b="b"))
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `str`"
@@ -1285,16 +1183,11 @@ The keys of the mapping passed to a double-starred argument must be strings.
```py
from collections.abc import Mapping
def f(**kwargs: int) -> None: ...
class DictSubclass(dict[int, int]): ...
class MappingSubclass(Mapping[int, int]): ...
class MappingProtocol:
def keys(self) -> list[int]:
return [1]
@@ -1302,12 +1195,10 @@ class MappingProtocol:
def __getitem__(self, key: int) -> int:
return 1
def _(kwargs: dict[int, int]) -> None:
# error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
f(**kwargs)
# error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
f(**DictSubclass())
# error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
@@ -1320,11 +1211,8 @@ The key can also be a custom type that inherits from `str`.
```py
class SubStr(str): ...
class SubInt(int): ...
def _(kwargs1: dict[SubStr, int], kwargs2: dict[SubInt, int]) -> None:
f(**kwargs1)
# error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `SubInt`"
@@ -1337,7 +1225,6 @@ Or, it can be a type that is assignable to `str`.
from typing import Any
from ty_extensions import Unknown
def _(kwargs1: dict[Any, int], kwargs2: dict[Unknown, int]) -> None:
f(**kwargs1)
f(**kwargs2)
@@ -1348,16 +1235,11 @@ def _(kwargs1: dict[Any, int], kwargs2: dict[Unknown, int]) -> None:
```py
from collections.abc import Mapping
def f(**kwargs: str) -> None: ...
class DictSubclass(dict[str, int]): ...
class MappingSubclass(Mapping[str, int]): ...
class MappingProtocol:
def keys(self) -> list[str]:
return ["foo"]
@@ -1365,7 +1247,6 @@ class MappingProtocol:
def __getitem__(self, key: str) -> int:
return 1
def _(kwargs: dict[str, int]) -> None:
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
f(**kwargs)
@@ -1382,7 +1263,6 @@ def _(kwargs: dict[str, int]) -> None:
```py
from ty_extensions import Unknown
def f(**kwargs: int) -> None: ...
def _(kwargs: Unknown):
f(**kwargs)
@@ -1393,10 +1273,8 @@ def _(kwargs: Unknown):
```py
def f(**kwargs: int) -> None: ...
class A: ...
class InvalidMapping:
def keys(self) -> A:
return A()
@@ -1404,7 +1282,6 @@ class InvalidMapping:
def __getitem__(self, key: str) -> int:
return 1
def _(kwargs: dict[str, int] | int):
# error: [invalid-argument-type] "Argument expression after ** must be a mapping type: Found `dict[str, int] | int`"
f(**kwargs)
@@ -1422,11 +1299,9 @@ from typing import TypeVar
_T = TypeVar("_T")
def f(**kwargs: _T) -> _T:
return kwargs["a"]
def _(kwargs: dict[str, int]) -> None:
reveal_type(f(**kwargs)) # revealed: int
```
@@ -1439,15 +1314,12 @@ from typing_extensions import TypedDict
_T = TypeVar("_T")
class Foo(TypedDict):
a: int
b: str
def f(**kwargs: _T) -> _T:
return kwargs["a"]
reveal_type(f(**Foo(a=1, b="b"))) # revealed: int | str
```

View File

@@ -10,12 +10,10 @@ Consider the following example:
```py
import inspect
class Descriptor:
def __get__(self, instance, owner) -> str:
return "a"
class C:
normal: int = 1
descriptor: Descriptor = Descriptor()
@@ -72,7 +70,6 @@ class D:
def __init__(self) -> None:
self.instance_attr: int = 1
reveal_type(inspect.getattr_static(D(), "instance_attr")) # revealed: int
```
@@ -82,10 +79,8 @@ And attributes on metaclasses can be accessed when probing the class:
class Meta(type):
attr: int = 1
class E(metaclass=Meta): ...
reveal_type(inspect.getattr_static(E, "attr")) # revealed: int
```
@@ -103,11 +98,9 @@ back to `Any`:
```py
import inspect
class C:
x: int = 1
def _(attr_name: str):
reveal_type(inspect.getattr_static(C(), attr_name)) # revealed: Any
reveal_type(inspect.getattr_static(C(), attr_name, 1)) # revealed: Any
@@ -134,7 +127,6 @@ inspect.getattr_static(C(), "x", "default-arg", "one too many")
```py
import inspect
def _(flag: bool):
class C:
if flag:
@@ -149,7 +141,6 @@ def _(flag: bool):
import inspect
from typing import Any
def _(a: Any, tuple_of_any: tuple[Any]):
reveal_type(inspect.getattr_static(a, "x", "default")) # revealed: Any | Literal["default"]

View File

@@ -83,7 +83,6 @@ When we access methods from derived classes, they will be bound to instances of
class D(C):
pass
reveal_type(D().f) # revealed: bound method D.f(x: int) -> str
```
@@ -108,12 +107,10 @@ class Base:
def method_on_base(self, x: int | None) -> str:
return "a"
class Derived(Base):
def method_on_derived(self, x: bytes) -> tuple[int, str]:
return (1, "a")
reveal_type(Base().method_on_base(1)) # revealed: str
reveal_type(Base.method_on_base(Base(), 1)) # revealed: str
@@ -162,7 +159,6 @@ reveal_type(b"abcde".startswith(b"abc")) # revealed: bool
```py
from typing_extensions import LiteralString
def f(s: LiteralString) -> None:
reveal_type(s.find("a")) # revealed: int
```
@@ -179,17 +175,14 @@ def f(t: tuple[int, str]) -> None:
```py
from typing import Any
class A:
def f(self) -> int:
return 1
class B:
def f(self) -> str:
return "a"
def f(a_or_b: A | B, any_or_a: Any | A):
reveal_type(a_or_b.f) # revealed: (bound method A.f() -> int) | (bound method B.f() -> str)
reveal_type(a_or_b.f()) # revealed: int | str
@@ -223,18 +216,14 @@ class:
from typing import Protocol, Literal
from ty_extensions import AlwaysFalsy
class Foo: ...
class SupportsStr(Protocol):
def __str__(self) -> str: ...
class Falsy(Protocol):
def __bool__(self) -> Literal[False]: ...
def _(a: object, b: SupportsStr, c: Falsy, d: AlwaysFalsy, e: None, f: Foo | None):
a.__str__()
b.__str__()
@@ -264,12 +253,10 @@ Here, we test that this signature is enforced correctly:
```py
from inspect import getattr_static
class C:
def f(self, x: int) -> str:
return "a"
method_wrapper = getattr_static(C, "f").__get__
reveal_type(method_wrapper) # revealed: <method-wrapper '__get__' of function 'f'>
@@ -311,16 +298,13 @@ the class itself. This also creates a bound method that is bound to the class ob
```py
from __future__ import annotations
class Meta(type):
def f(cls, arg: int) -> str:
return "a"
class C(metaclass=Meta):
pass
reveal_type(C.f) # revealed: bound method <class 'C'>.f(arg: int) -> str
reveal_type(C.f(1)) # revealed: str
```
@@ -337,12 +321,10 @@ A metaclass function can be shadowed by a method on the class:
```py
from typing import Any, Literal
class D(metaclass=Meta):
def f(arg: int) -> Literal["a"]:
return "a"
reveal_type(D.f(1)) # revealed: Literal["a"]
```
@@ -352,14 +334,11 @@ If the class method is possibly missing, we union the return types:
def flag() -> bool:
return True
class E(metaclass=Meta):
if flag():
def f(arg: int) -> Any:
return "a"
reveal_type(E.f(1)) # revealed: str | Any
```
@@ -373,13 +352,11 @@ the class object itself:
```py
from __future__ import annotations
class C:
@classmethod
def f(cls: type[C], x: int) -> str:
return "a"
reveal_type(C.f) # revealed: bound method <class 'C'>.f(x: int) -> str
reveal_type(C().f) # revealed: bound method type[C].f(x: int) -> str
```
@@ -408,7 +385,6 @@ class D:
# This function is wrongly annotated, it should be `type[D]` instead of `D`
pass
# error: [invalid-argument-type] "Argument to bound method `f` is incorrect: Expected `D`, found `<class 'D'>`"
D.f()
```
@@ -419,7 +395,6 @@ When a class method is accessed on a derived class, it is bound to that derived
class Derived(C):
pass
reveal_type(Derived.f) # revealed: bound method <class 'Derived'>.f(x: int) -> str
reveal_type(Derived().f) # revealed: bound method type[Derived].f(x: int) -> str
@@ -435,12 +410,10 @@ currently don't model this explicitly:
```py
from inspect import getattr_static
class C:
@classmethod
def f(cls): ...
reveal_type(getattr_static(C, "f")) # revealed: def f(cls) -> Unknown
# revealed: <method-wrapper '__get__' of function 'f'>
reveal_type(getattr_static(C, "f").__get__)
@@ -474,7 +447,6 @@ class method:
def does_nothing[T](f: T) -> T:
return f
class C:
@classmethod
@does_nothing
@@ -486,254 +458,28 @@ class C:
def f2(cls, x: int) -> str:
return "a"
reveal_type(C.f1(1)) # revealed: str
reveal_type(C().f1(1)) # revealed: str
reveal_type(C.f2(1)) # revealed: str
reveal_type(C().f2(1)) # revealed: str
```
### Classmethods with `Self` and callable-returning decorators
When a classmethod is decorated with a decorator that returns a callable type (like
`@contextmanager`), `Self` in the return type should correctly resolve to the subclass when accessed
on a derived class.
```py
from contextlib import contextmanager
from typing import Iterator
from typing_extensions import Self
class Base:
@classmethod
@contextmanager
def create(cls) -> Iterator[Self]:
yield cls()
class Child(Base): ...
reveal_type(Base.create()) # revealed: _GeneratorContextManager[Base, None, None]
with Base.create() as base:
reveal_type(base) # revealed: Base
reveal_type(Base().create()) # revealed: _GeneratorContextManager[Base, None, None]
with Base().create() as base:
reveal_type(base) # revealed: Base
reveal_type(Child.create()) # revealed: _GeneratorContextManager[Child, None, None]
with Child.create() as child:
reveal_type(child) # revealed: Child
reveal_type(Child().create()) # revealed: _GeneratorContextManager[Child, None, None]
with Child().create() as child:
reveal_type(child) # revealed: Child
```
### `__init_subclass__`
The [`__init_subclass__`] method is implicitly a classmethod:
```toml
[environment]
python-version = "3.12"
```
```py
class Base:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.custom_attribute: int = 0
class Derived(Base):
pass
reveal_type(Derived.custom_attribute) # revealed: int
```
Subclasses must be constructed with arguments matching the required arguments of the base
`__init_subclass__` method.
```py
class Empty: ...
class RequiresArg:
def __init_subclass__(cls, arg: int): ...
class NoArg:
def __init_subclass__(cls): ...
# Single-base definitions
class MissingArg(RequiresArg): ... # error: [missing-argument]
class InvalidType(RequiresArg, arg="foo"): ... # error: [invalid-argument-type]
class Valid(RequiresArg, arg=1): ...
# error: [missing-argument]
# error: [unknown-argument]
class IncorrectArg(RequiresArg, not_arg="foo"): ...
```
For multiple inheritance, the first resolved `__init_subclass__` method is used.
```py
class Empty: ...
class RequiresArg:
def __init_subclass__(cls, arg: int): ...
class NoArg:
def __init_subclass__(cls): ...
class Valid(NoArg, RequiresArg): ...
class MissingArg(RequiresArg, NoArg): ... # error: [missing-argument]
class InvalidType(RequiresArg, NoArg, arg="foo"): ... # error: [invalid-argument-type]
class Valid(RequiresArg, NoArg, arg=1): ...
# Ensure base class without __init_subclass__ is ignored
class Valid(Empty, NoArg): ...
class Valid(Empty, RequiresArg, NoArg, arg=1): ...
class MissingArg(Empty, RequiresArg): ... # error: [missing-argument]
class MissingArg(Empty, RequiresArg, NoArg): ... # error: [missing-argument]
class InvalidType(Empty, RequiresArg, NoArg, arg="foo"): ... # error: [invalid-argument-type]
# Multiple inheritance with args
class Base(Empty, RequiresArg, NoArg, arg=1): ...
class Valid(Base, arg=1): ...
class MissingArg(Base): ... # error: [missing-argument]
class InvalidType(Base, arg="foo"): ... # error: [invalid-argument-type]
```
Keyword splats are allowed if their type can be determined:
```py
from typing import TypedDict
class RequiresKwarg:
def __init_subclass__(cls, arg: int): ...
class WrongArg(TypedDict):
kwarg: int
class InvalidType(TypedDict):
arg: str
wrong_arg: WrongArg = {"kwarg": 5}
# error: [missing-argument]
# error: [unknown-argument]
class MissingArg(RequiresKwarg, **wrong_arg): ...
invalid_type: InvalidType = {"arg": "foo"}
# error: [invalid-argument-type]
class InvalidType(RequiresKwarg, **invalid_type): ...
```
So are generics:
```py
from typing import Generic, TypeVar, Literal, overload
class Base[T]:
def __init_subclass__(cls, arg: T): ...
class Valid(Base[int], arg=1): ...
class InvalidType(Base[int], arg="x"): ... # error: [invalid-argument-type]
# Old generic syntax
T = TypeVar("T")
class Base(Generic[T]):
def __init_subclass__(cls, arg: T) -> None: ...
class Valid(Base[int], arg=1): ...
class InvalidType(Base[int], arg="x"): ... # error: [invalid-argument-type]
```
So are overloads:
```py
class Base:
@overload
def __init_subclass__(cls, mode: Literal["a"], arg: int) -> None: ...
@overload
def __init_subclass__(cls, mode: Literal["b"], arg: str) -> None: ...
def __init_subclass__(cls, mode: str, arg: int | str) -> None: ...
class Valid(Base, mode="a", arg=5): ...
class Valid(Base, mode="b", arg="foo"): ...
class InvalidType(Base, mode="b", arg=5): ... # error: [no-matching-overload]
```
The `metaclass` keyword is ignored, as it has special meaning and is not passed to
`__init_subclass__` at runtime.
```py
class Base:
def __init_subclass__(cls, arg: int): ...
class Valid(Base, arg=5, metaclass=object): ...
```
## `@staticmethod`
### Basic
@@ -744,13 +490,11 @@ true whether it's accessed on the class or on an instance of the class.
```py
from __future__ import annotations
class C:
@staticmethod
def f(x: int) -> str:
return "a"
reveal_type(C.f) # revealed: def f(x: int) -> str
reveal_type(C().f) # revealed: def f(x: int) -> str
```
@@ -777,7 +521,6 @@ When a static method is accessed on a derived class, it behaves identically:
class Derived(C):
pass
reveal_type(Derived.f) # revealed: def f(x: int) -> str
reveal_type(Derived().f) # revealed: def f(x: int) -> str
@@ -790,7 +533,6 @@ reveal_type(Derived().f(1)) # revealed: str
```py
from inspect import getattr_static
class C:
@staticmethod
def f(): ...
@@ -826,11 +568,9 @@ static method:
```py
from __future__ import annotations
def does_nothing[T](f: T) -> T:
return f
class C:
@staticmethod
@does_nothing
@@ -842,7 +582,6 @@ class C:
def f2(x: int) -> str:
return "a"
reveal_type(C.f1(1)) # revealed: str
reveal_type(C().f1(1)) # revealed: str
reveal_type(C.f2(1)) # revealed: str
@@ -856,7 +595,6 @@ bind `self`:
from contextlib import contextmanager
from collections.abc import Iterator
class D:
@staticmethod
@contextmanager
@@ -868,7 +606,6 @@ class D:
with self.ctx(10) as x:
reveal_type(x) # revealed: int
# Accessing via class works
reveal_type(D.ctx(5)) # revealed: _GeneratorContextManager[int, None, None]
@@ -884,18 +621,17 @@ argument:
```py
from typing_extensions import Self
reveal_type(object.__new__) # revealed: def __new__[Self](cls) -> Self
reveal_type(object().__new__) # revealed: def __new__[Self](cls) -> Self
# revealed: Overload[[Self](cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = 0, /) -> Self, [Self](cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self]
reveal_type(object.__new__) # revealed: def __new__(cls) -> Self@__new__
reveal_type(object().__new__) # revealed: def __new__(cls) -> Self@__new__
# revealed: Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = 0, /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
reveal_type(int.__new__)
# revealed: Overload[[Self](cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = 0, /) -> Self, [Self](cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self]
# revealed: Overload[(cls, x: str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = 0, /) -> Self@__new__, (cls, x: str | bytes | bytearray, /, base: SupportsIndex) -> Self@__new__]
reveal_type((42).__new__)
class X:
def __init__(self, val: int): ...
def make_another(self) -> Self:
reveal_type(self.__new__) # revealed: def __new__[Self](cls) -> Self
reveal_type(self.__new__) # revealed: def __new__(cls) -> Self@__new__
return self.__new__(type(self))
```
@@ -909,10 +645,8 @@ import types
from typing import Callable
from ty_extensions import static_assert, CallableTypeOf, is_assignable_to, TypeOf
def f(obj: type) -> None: ...
class MyClass:
@property
def my_property(self) -> int:
@@ -921,7 +655,6 @@ class MyClass:
@my_property.setter
def my_property(self, value: int | str) -> None: ...
static_assert(is_assignable_to(types.FunctionType, Callable))
# revealed: <wrapper-descriptor '__get__' of 'function' objects>
@@ -973,7 +706,6 @@ static_assert(is_assignable_to(TypeOf[str.startswith], Callable))
reveal_type("foo".startswith)
static_assert(is_assignable_to(TypeOf["foo".startswith], Callable))
def _(
a: CallableTypeOf[types.FunctionType.__get__],
b: CallableTypeOf[f],

View File

@@ -5,7 +5,6 @@ The type `Never` is callable with an arbitrary set of arguments. The result is a
```py
from typing_extensions import Never
def f(never: Never):
reveal_type(never()) # revealed: Never
reveal_type(never(1)) # revealed: Never

View File

@@ -15,7 +15,6 @@ reveal_type(open("", "rb")) # revealed: BufferedReader[_BufferedReaderStream]
with open("foo.pickle", "rb") as f:
x = pickle.load(f) # fine
def _(mode: str):
reveal_type(open("", mode)) # revealed: IO[Any]
```

View File

@@ -174,7 +174,6 @@ def f(x: C) -> C: ...
```py
from overloaded import A, B, C, f
def _(ab: A | B, ac: A | C, bc: B | C):
reveal_type(f(ab)) # revealed: A | B
reveal_type(f(*(ab,))) # revealed: A | B
@@ -214,7 +213,6 @@ def f(x: B, y: D) -> D: ...
```py
from overloaded import A, B, C, D, f
def _(a_b: A | B):
reveal_type(f(a_b, C())) # revealed: A | C
reveal_type(f(*(a_b, C()))) # revealed: A | C
@@ -222,7 +220,6 @@ def _(a_b: A | B):
reveal_type(f(a_b, D())) # revealed: B | D
reveal_type(f(*(a_b, D()))) # revealed: B | D
# But, if it doesn't, it should expand the second argument and try again:
def _(a_b: A | B, c_d: C | D):
reveal_type(f(a_b, c_d)) # revealed: A | B | C | D
@@ -255,7 +252,6 @@ def f(x: B, y: D) -> D: ...
```py
from overloaded import A, B, C, D, f
def _(a: A, bc: B | C, cd: C | D):
# This also tests that partial matching works correctly as the argument type expansion results
# in matching the first and second overloads, but not the third one.
@@ -289,7 +285,6 @@ def f(x: _T) -> _T: ...
```py
from overloaded import A, f
def _(x: int, y: A | int):
reveal_type(f(x)) # revealed: int
reveal_type(f(*(x,))) # revealed: int
@@ -322,7 +317,6 @@ def f[T](x: T) -> T: ...
```py
from overloaded import B, f
def _(x: int, y: B | int):
reveal_type(f(x)) # revealed: int
reveal_type(f(*(x,))) # revealed: int
@@ -350,7 +344,6 @@ def f(x: Literal[False]) -> F: ...
```py
from overloaded import f
def _(flag: bool):
reveal_type(f(True)) # revealed: T
reveal_type(f(*(True,))) # revealed: T
@@ -387,7 +380,6 @@ def f(x: tuple[B, int], y: tuple[int, Literal[False]]) -> D: ...
```py
from overloaded import A, B, f
def _(x: tuple[A | B, int], y: tuple[int, bool]):
reveal_type(f(x, y)) # revealed: A | B | C | D
reveal_type(f(*(x, y))) # revealed: A | B | C | D
@@ -415,7 +407,6 @@ def f(x: type[B]) -> B: ...
```py
from overloaded import A, B, f
def _(x: type[A | B]):
reveal_type(x) # revealed: type[A] | type[B]
reveal_type(f(x)) # revealed: A | B
@@ -454,7 +445,6 @@ def f(x: Literal[SomeEnum.C]) -> C: ...
from typing import Literal
from overloaded import SomeEnum, A, B, C, f
def _(x: SomeEnum, y: Literal[SomeEnum.A, SomeEnum.C]):
reveal_type(f(SomeEnum.A)) # revealed: A
reveal_type(f(*(SomeEnum.A,))) # revealed: A
@@ -508,7 +498,6 @@ reveal_type(f(b=0)) # revealed: OnlyBSpecified
f(a=0, b=0) # error: [no-matching-overload]
def _(missing: Literal[Missing.Value], missing_or_present: Literal[Missing.Value] | int):
reveal_type(f(a=missing, b=missing)) # revealed: BothMissing
reveal_type(f(a=missing)) # revealed: BothMissing
@@ -557,7 +546,6 @@ def f(x: MyEnumSubclass) -> MyEnumSubclass: ...
```py
from overloaded import MyEnumSubclass, ActualEnum, f
def _(actual_enum: ActualEnum, my_enum_instance: MyEnumSubclass):
reveal_type(f(actual_enum)) # revealed: Both
reveal_type(f(*(actual_enum,))) # revealed: Both
@@ -596,7 +584,6 @@ def f(x: B) -> B: ...
```py
from overloaded import A, B, C, D, f
def _(ab: A | B, ac: A | C, cd: C | D):
reveal_type(f(ab)) # revealed: A | B
reveal_type(f(*(ab,))) # revealed: A | B
@@ -657,7 +644,6 @@ class Foo:
from overloaded import A, B, C, Foo, f
from typing_extensions import Any, reveal_type
def _(ab: A | B, a: int | Any):
reveal_type(f(a1=a, a2=a, a3=a)) # revealed: C
reveal_type(f(A(), a1=a, a2=a, a3=a)) # revealed: A
@@ -746,7 +732,6 @@ def _(ab: A | B, a: int | Any):
)
)
def _(foo: Foo, ab: A | B, a: int | Any):
reveal_type(foo.f(a1=a, a2=a, a3=a)) # revealed: C
reveal_type(foo.f(A(), a1=a, a2=a, a3=a)) # revealed: A
@@ -858,7 +843,6 @@ def f(x: B, /, **kwargs: int) -> B: ...
from overloaded import A, B, f
from typing_extensions import reveal_type
def _(a: int | None):
reveal_type(
# error: [no-matching-overload]
@@ -922,20 +906,17 @@ from overloaded import f
# Test all of the above with a number of different splatted argument types
def _(t: tuple[int, str]) -> None:
# This correctly produces an error because the first element of the union has a precise arity of
# 2, which matches the first overload, but the second element of the tuple doesn't match the
# second parameter type, yielding an `invalid-argument-type` error.
f(*t) # error: [invalid-argument-type]
def _(t: tuple[int, str, int]) -> None:
# This correctly produces no error because the first element of the union has a precise arity of
# 3, which matches the second overload.
f(*t)
def _(t: tuple[int, str] | tuple[int, str, int]) -> None:
# This produces an error because the expansion produces two argument lists: `[*tuple[int, str]]`
# and `[*tuple[int, str, int]]`. The first list produces produces a type checking error as
@@ -973,7 +954,6 @@ def f(*args: int) -> int: ...
```py
from overloaded import f
def _(x1: int, x2: int, args: list[int]):
reveal_type(f(x1)) # revealed: tuple[int]
reveal_type(f(x1, x2)) # revealed: tuple[int, int]
@@ -1006,7 +986,6 @@ def f(x1: int, *args: int) -> tuple[int, ...]: ...
```py
from overloaded import f
def _(x1: int, x2: int, args1: list[int], args2: tuple[int, *tuple[int, ...]]):
reveal_type(f(x1, x2)) # revealed: tuple[int, int]
reveal_type(f(*(x1, x2))) # revealed: tuple[int, int]
@@ -1034,7 +1013,6 @@ def f(**kwargs: int) -> int: ...
```py
from overloaded import f
def _(x1: int, x2: int, kwargs: dict[str, int]):
reveal_type(f(x1=x1)) # revealed: int
reveal_type(f(x1=x1, x2=x2)) # revealed: tuple[int, int]
@@ -1066,12 +1044,10 @@ def f(**kwargs: int) -> tuple[int, ...]: ...
from typing import TypedDict
from overloaded import f
class Foo(TypedDict):
x: int
y: int
def _(foo: Foo, kwargs: dict[str, int]):
reveal_type(f(**foo)) # revealed: tuple[int, int]
reveal_type(f(**kwargs)) # revealed: tuple[int, ...]
@@ -1113,7 +1089,6 @@ from overloaded import f
reveal_type(f(1)) # revealed: str
reveal_type(f(*(1,))) # revealed: str
def _(list_int: list[int], list_any: list[Any]):
reveal_type(f(list_int)) # revealed: int
reveal_type(f(*(list_int,))) # revealed: int
@@ -1149,7 +1124,6 @@ from overloaded import f
reveal_type(f(1)) # revealed: str
reveal_type(f(*(1,))) # revealed: str
def _(list_int: list[int], list_any: list[Any]):
# All materializations of `list[int]` are assignable to `list[int]`, so it matches the first
# overload.
@@ -1192,7 +1166,6 @@ reveal_type(f(*((1, "b"),))) # revealed: int
reveal_type(f((1, 2))) # revealed: int
reveal_type(f(*((1, 2),))) # revealed: int
def _(int_str: tuple[int, str], int_any: tuple[int, Any], any_any: tuple[Any, Any]):
# All materializations are assignable to first overload, so second and third overloads are
# eliminated
@@ -1233,7 +1206,6 @@ class Foo:
from module import Foo
from typing_extensions import LiteralString
def f(a: Foo, b: list[str], c: list[LiteralString], e):
reveal_type(e) # revealed: Unknown
reveal_type(a.join(b)) # revealed: str
@@ -1273,7 +1245,6 @@ from typing import Any
from overloaded import A, f
def _(list_int: list[int], list_any: list[Any], int_str: tuple[int, str], int_any: tuple[int, Any], any_any: tuple[Any, Any]):
# All materializations of both argument types are assignable to the first overload, so the
# second and third overloads are filtered out
@@ -1322,7 +1293,6 @@ from typing_extensions import LiteralString
from overloaded import f
def _(literal: LiteralString, string: str, any: Any):
reveal_type(f(literal)) # revealed: LiteralString
reveal_type(f(*(literal,))) # revealed: LiteralString
@@ -1361,7 +1331,6 @@ from typing import Any
from overloaded import f
def _(list_int: list[int], list_str: list[str], list_any: list[Any], any: Any):
reveal_type(f(list_int)) # revealed: A
reveal_type(f(*(list_int,))) # revealed: A
@@ -1396,7 +1365,6 @@ from typing import Any
from overloaded import f
def _(integer: int, string: str, any: Any, list_any: list[Any]):
reveal_type(f(integer, string)) # revealed: int
reveal_type(f(*(integer, string))) # revealed: int
@@ -1439,13 +1407,11 @@ from typing import Any
from overloaded import A, B
def _(a_int: A[int], a_str: A[str], a_any: A[Any]):
reveal_type(a_int.method()) # revealed: int
reveal_type(a_str.method()) # revealed: int
reveal_type(a_any.method()) # revealed: int
def _(b_int: B[int], b_str: B[str], b_any: B[Any]):
reveal_type(b_int.method()) # revealed: int
reveal_type(b_str.method()) # revealed: str
@@ -1492,7 +1458,6 @@ from typing import Any
from overloaded import f1, f2, f3, f4
def _(arg: list[Any]):
# Matches both overload and the return types are equivalent
reveal_type(f1(*arg)) # revealed: A
@@ -1555,12 +1520,10 @@ reveal_type(f1(1)) # revealed: tuple[Literal[1]]
reveal_type(f1(1, 2)) # revealed: tuple[Literal[1], Literal[2]]
reveal_type(f1(1, 2, 3)) # revealed: tuple[Literal[1], Literal[2], Literal[3]]
def _(args1: list[int], args2: list[Any]):
reveal_type(f1(*args1)) # revealed: tuple[Any, ...]
reveal_type(f1(*args2)) # revealed: tuple[Any, ...]
reveal_type(f2()) # revealed: tuple[Any, ...]
reveal_type(f2(1, 2)) # revealed: tuple[Literal[1], Literal[2]]
# TODO: Should be `tuple[Literal[1], Literal[2]]`
@@ -1611,7 +1574,6 @@ from typing import Any
from overloaded import f
def _(any: Any):
reveal_type(f(any, flag=True)) # revealed: int
reveal_type(f(*(any,), flag=True)) # revealed: int
@@ -1640,7 +1602,6 @@ from typing import Any, Literal
from overloaded import f
def _(any: Any):
reveal_type(f(any, flag=True)) # revealed: int
reveal_type(f(*(any,), flag=True)) # revealed: int
@@ -1648,11 +1609,9 @@ def _(any: Any):
reveal_type(f(any, flag=False)) # revealed: str
reveal_type(f(*(any,), flag=False)) # revealed: str
def _(args: tuple[Any, Literal[True]]):
reveal_type(f(*args)) # revealed: int
def _(args: tuple[Any, Literal[False]]):
reveal_type(f(*args)) # revealed: str
```
@@ -1697,7 +1656,6 @@ from typing import Any
from overloaded import A, B, f
def _(arg: tuple[A | B, Any]):
reveal_type(f(arg)) # revealed: A | B
reveal_type(f(*(arg,))) # revealed: A | B
@@ -1733,7 +1691,6 @@ from typing import Any
from overloaded import A, B, C, f
def _(arg: tuple[A | B, Any]):
reveal_type(f(arg)) # revealed: A | Unknown
reveal_type(f(*(arg,))) # revealed: A | Unknown
@@ -1768,7 +1725,6 @@ from typing import Any
from overloaded import A, B, C, f
def _(arg: tuple[A | B, Any]):
reveal_type(f(arg)) # revealed: Unknown
reveal_type(f(*(arg,))) # revealed: Unknown
@@ -1786,11 +1742,9 @@ Type inference accounts for parameter type annotations across all overloads.
```py
from typing import TypedDict, overload
class T(TypedDict):
x: int
@overload
def f(a: list[T], b: int) -> int: ...
@overload
@@ -1798,11 +1752,9 @@ def f(a: list[dict[str, int]], b: str) -> str: ...
def f(a: list[dict[str, int]] | list[T], b: int | str) -> int | str:
return 1
def int_or_str() -> int | str:
return 1
x = f([{"x": 1}], int_or_str())
reveal_type(x) # revealed: int | str
@@ -1815,11 +1767,9 @@ Non-matching overloads do not produce diagnostics:
```py
from typing import TypedDict, overload
class T(TypedDict):
x: int
@overload
def f(a: T, b: int) -> int: ...
@overload
@@ -1827,7 +1777,6 @@ def f(a: dict[str, int], b: str) -> str: ...
def f(a: T | dict[str, int], b: int | str) -> int | str:
return 1
x = f({"y": 1}, "a")
reveal_type(x) # revealed: str
```
@@ -1835,13 +1784,11 @@ reveal_type(x) # revealed: str
```py
from typing import SupportsRound, overload
@overload
def takes_str_or_float(x: str): ...
@overload
def takes_str_or_float(x: float): ...
def takes_str_or_float(x: float | str): ...
takes_str_or_float(round(1.0))
```

View File

@@ -31,13 +31,11 @@ Dataclasses support the `__replace__` protocol:
from dataclasses import dataclass
from copy import replace
@dataclass
class Point:
x: int
y: int
reveal_type(Point.__replace__) # revealed: (self: Point, *, x: int = ..., y: int = ...) -> Point
```
@@ -82,12 +80,10 @@ NamedTuples also support the `__replace__` protocol:
from typing import NamedTuple
from copy import replace
class Point(NamedTuple):
x: int
y: int
reveal_type(Point.__replace__) # revealed: (self: Self, *, x: int = ..., y: int = ...) -> Self
```

View File

@@ -39,7 +39,6 @@ And similiarly, we should still infer `bool` if the instance or the prefix are n
```py
from typing_extensions import LiteralString
def _(string_instance: str, literalstring: LiteralString):
reveal_type(string_instance.startswith("a")) # revealed: bool
reveal_type(literalstring.startswith("a")) # revealed: bool

View File

@@ -7,7 +7,6 @@
```py
class C: ...
def _(subclass_of_c: type[C]):
reveal_type(subclass_of_c()) # revealed: C
```
@@ -18,7 +17,6 @@ def _(subclass_of_c: type[C]):
class C:
def __init__(self, x: int): ...
def _(subclass_of_c: type[C]):
reveal_type(subclass_of_c(1)) # revealed: C
@@ -36,7 +34,6 @@ def _(subclass_of_c: type[C]):
from typing import Any
from ty_extensions import Unknown
def _(subclass_of_any: type[Any], subclass_of_unknown: type[Unknown]):
reveal_type(subclass_of_any()) # revealed: Any
reveal_type(subclass_of_any("any", "args", 1, 2)) # revealed: Any
@@ -48,11 +45,8 @@ def _(subclass_of_any: type[Any], subclass_of_unknown: type[Unknown]):
```py
class A: ...
class B: ...
def _(subclass_of_ab: type[A | B]):
reveal_type(subclass_of_ab()) # revealed: A | B
```

View File

@@ -17,11 +17,8 @@ from the first argument:
```py
class Base: ...
class Mixin: ...
# We synthesize a class type using the name argument
Foo = type("Foo", (), {})
reveal_type(Foo) # revealed: <class 'Foo'>
@@ -47,11 +44,9 @@ name = "IndirectClass"
IndirectClass = type(name, (), {})
reveal_type(IndirectClass) # revealed: <class 'IndirectClass'>
# Works with base classes too
class Base: ...
base_name = "DerivedClass"
DerivedClass = type(base_name, (Base,), {})
reveal_type(DerivedClass) # revealed: <class 'DerivedClass'>
@@ -64,10 +59,8 @@ Each `type()` call produces a distinct class type, even if they have the same na
```py
from ty_extensions import static_assert, is_equivalent_to
class Base: ...
Foo1 = type("Foo", (Base,), {})
Foo2 = type("Foo", (Base,), {})
@@ -78,17 +71,15 @@ static_assert(not is_equivalent_to(Foo1, Foo2))
foo1 = Foo1()
foo2 = Foo2()
def takes_foo1(x: Foo1) -> None: ...
def takes_foo2(x: Foo2) -> None: ...
takes_foo1(foo1) # OK
takes_foo2(foo2) # OK
# error: [invalid-argument-type] "Argument to function `takes_foo1` is incorrect: Expected `mdtest_snippet.Foo @ src/mdtest_snippet.py:5:8`, found `mdtest_snippet.Foo @ src/mdtest_snippet.py:6:8`"
# error: [invalid-argument-type] "Argument to function `takes_foo1` is incorrect: Expected `mdtest_snippet.Foo @ src/mdtest_snippet.py:5`, found `mdtest_snippet.Foo @ src/mdtest_snippet.py:6`"
takes_foo1(foo2)
# error: [invalid-argument-type] "Argument to function `takes_foo2` is incorrect: Expected `mdtest_snippet.Foo @ src/mdtest_snippet.py:6:8`, found `mdtest_snippet.Foo @ src/mdtest_snippet.py:5:8`"
# error: [invalid-argument-type] "Argument to function `takes_foo2` is incorrect: Expected `mdtest_snippet.Foo @ src/mdtest_snippet.py:6`, found `mdtest_snippet.Foo @ src/mdtest_snippet.py:5`"
takes_foo2(foo1)
```
@@ -104,11 +95,9 @@ class Base:
def base_method(self) -> str:
return "hello"
class Mixin:
mixin_attr: str = "mixin"
Foo = type("Foo", (Base,), {})
foo = Foo()
@@ -126,124 +115,17 @@ reveal_type(bar.base_attr) # revealed: int
reveal_type(bar.mixin_attr) # revealed: str
```
Attributes from the namespace dict (third argument) are tracked:
Attributes from the namespace dict (third argument) are not tracked. Like Pyright, we error when
attempting to access them:
```py
class Base: ...
Foo = type("Foo", (Base,), {"custom_attr": 42})
# Class attribute access
reveal_type(Foo.custom_attr) # revealed: Literal[42]
# Instance attribute access
foo = Foo()
reveal_type(foo.custom_attr) # revealed: Literal[42]
```
When the namespace dict is not a literal (e.g., passed as a parameter), attribute access returns
`Unknown` since we can't know what attributes might be defined:
```py
from typing import Any
class DynamicBase: ...
def f(attributes: dict[str, Any]):
X = type("X", (DynamicBase,), attributes)
reveal_type(X) # revealed: <class 'X'>
# Attribute access returns Unknown since the namespace is dynamic
reveal_type(X.foo) # revealed: Unknown
x = X()
reveal_type(x.bar) # revealed: Unknown
```
When a namespace dictionary is partially dynamic (e.g., a dict literal with spread or non-literal
keys), static attributes have precise types while unknown attributes return `Unknown`:
```py
from typing import Any
def f(extra_attrs: dict[str, Any], y: str):
X = type("X", (), {"a": 42, **extra_attrs})
# Static attributes in the namespace dictionary have precise types,
# but the dictionary was not entirely static, so other attributes
# are still available and resolve to `Unknown`:
reveal_type(X().a) # revealed: Literal[42]
reveal_type(X().whatever) # revealed: Unknown
Y = type("Y", (), {"a": 56, y: 72})
reveal_type(Y().a) # revealed: Literal[56]
reveal_type(Y().whatever) # revealed: Unknown
```
When a `TypedDict` is passed as the namespace argument, we synthesize a class type with the known
keys from the `TypedDict` as attributes. Since `TypedDict` instances are "open" (they can have
arbitrary additional string keys), unknown attributes return `Unknown`:
```py
from typing import TypedDict
class Namespace(TypedDict):
z: int
def g(attributes: Namespace):
Y = type("Y", (), attributes)
reveal_type(Y) # revealed: <class 'Y'>
# Known keys from the TypedDict are tracked as attributes
reveal_type(Y.z) # revealed: int
y = Y()
reveal_type(y.z) # revealed: int
# Unknown attributes return Unknown since TypedDicts are open
reveal_type(Y.unknown) # revealed: Unknown
reveal_type(y.unknown) # revealed: Unknown
```
## Closed TypedDicts (PEP-728)
TODO: We don't support the PEP-728 `closed=True` keyword argument to `TypedDict` yet. When we do, a
closed TypedDict namespace should NOT be marked as dynamic, and accessing unknown attributes should
emit an error instead of returning `Unknown`.
```py
from typing import TypedDict
class ClosedNamespace(TypedDict, closed=True):
x: int
y: str
def h(ns: ClosedNamespace):
X = type("X", (), ns)
reveal_type(X) # revealed: <class 'X'>
# Known keys from the TypedDict are tracked as attributes
reveal_type(X.x) # revealed: int
reveal_type(X.y) # revealed: str
x = X()
reveal_type(x.x) # revealed: int
reveal_type(x.y) # revealed: str
# TODO: Once we support `closed=True`, these should emit errors instead of returning Unknown
reveal_type(X.unknown) # revealed: Unknown
reveal_type(x.unknown) # revealed: Unknown
# error: [unresolved-attribute] "Object of type `Foo` has no attribute `custom_attr`"
reveal_type(foo.custom_attr) # revealed: Unknown
```
## Inheritance from dynamic classes
@@ -254,14 +136,11 @@ Regular classes can inherit from dynamic classes:
class Base:
base_attr: int = 1
DynamicClass = type("DynamicClass", (Base,), {})
class Child(DynamicClass):
child_attr: str = "child"
child = Child()
# Attributes from the dynamic class's base are accessible
@@ -270,24 +149,19 @@ reveal_type(child.base_attr) # revealed: int
# The child class's own attributes are accessible
reveal_type(child.child_attr) # revealed: str
# Child instances are subtypes of DynamicClass instances
def takes_dynamic(x: DynamicClass) -> None: ...
takes_dynamic(child) # No error - Child is a subtype of DynamicClass
# isinstance narrows to the dynamic class instance type
def check_isinstance(x: object) -> None:
if isinstance(x, DynamicClass):
reveal_type(x) # revealed: DynamicClass
# Dynamic class inheriting from int narrows correctly with isinstance
IntSubclass = type("IntSubclass", (int,), {})
def check_int_subclass(x: IntSubclass | str) -> None:
if isinstance(x, int):
# IntSubclass inherits from int, so it's included in the narrowed type
@@ -304,10 +178,8 @@ from both):
```py
class Base: ...
Foo = type("Foo", (Base,), {})
def check_disjointness(x: Foo | int) -> None:
if isinstance(x, int):
reveal_type(x) # revealed: int
@@ -328,11 +200,9 @@ StrClass = type("StrClass", (str,), {})
static_assert(is_disjoint_from(type[IntClass], type[StrClass]))
static_assert(is_disjoint_from(type[StrClass], type[IntClass]))
# Dynamic classes that share a common base are not disjoint.
class Base: ...
Foo = type("Foo", (Base,), {})
Bar = type("Bar", (Base,), {})
@@ -348,7 +218,6 @@ class Base:
def method(self) -> int:
return 42
DynamicChild = type("DynamicChild", (Base,), {})
# Using dynamic class as pivot with dynamic class instance owner
@@ -356,12 +225,10 @@ fc = DynamicChild()
reveal_type(super(DynamicChild, fc)) # revealed: <super: <class 'DynamicChild'>, DynamicChild>
reveal_type(super(DynamicChild, fc).method()) # revealed: int
# Regular class inheriting from dynamic class
class RegularChild(DynamicChild):
pass
rc = RegularChild()
reveal_type(super(RegularChild, rc)) # revealed: <super: <class 'RegularChild'>, RegularChild>
reveal_type(super(RegularChild, rc).method()) # revealed: int
@@ -379,7 +246,6 @@ Dynamic classes can inherit from other dynamic classes:
class Base:
base_attr: int = 1
# Create a dynamic class that inherits from a regular class.
Parent = type("Parent", (Base,), {})
reveal_type(Parent) # revealed: <class 'Parent'>
@@ -393,11 +259,9 @@ child = ChildCls()
reveal_type(child) # revealed: ChildCls
reveal_type(child.base_attr) # revealed: int
# Child instances are subtypes of `Parent` instances.
def takes_parent(x: Parent) -> None: ...
takes_parent(child) # No error - `ChildCls` is a subtype of `Parent`
```
@@ -410,14 +274,12 @@ dataclass-like and have the synthesized `__dataclass_fields__` attribute:
from dataclasses import Field
from typing_extensions import dataclass_transform
@dataclass_transform()
class DataclassBase:
"""Base class decorated with @dataclass_transform()."""
pass
# A dynamic class inheriting from a dataclass_transform base
DynamicModel = type("DynamicModel", (DataclassBase,), {})
@@ -447,11 +309,9 @@ from typing import Generic, TypeVar
T = TypeVar("T")
class Container(Generic[T]):
value: T
# Dynamic class inheriting from a generic class specialization
IntContainer = type("IntContainer", (Container[int],), {})
reveal_type(IntContainer) # revealed: <class 'IntContainer'>
@@ -468,7 +328,6 @@ reveal_type(container.value) # revealed: int
```py
class Base: ...
Foo = type("Foo", (Base,), {})
foo = Foo()
@@ -481,7 +340,6 @@ reveal_type(type(foo)) # revealed: type[Foo]
```py
class Base: ...
Foo = type("Foo", (Base,), {})
foo = Foo()
@@ -494,7 +352,6 @@ reveal_type(foo.__class__) # revealed: type[Foo]
```py
class StaticClass: ...
DynamicClass = type("DynamicClass", (), {})
# Both static and dynamic classes have `type` as their metaclass
@@ -509,15 +366,12 @@ Dynamic instances are subtypes of `object`:
```py
class Base: ...
Foo = type("Foo", (Base,), {})
foo = Foo()
# All dynamic instances are subtypes of object
def takes_object(x: object) -> None: ...
takes_object(foo) # No error - Foo is a subtype of object
# Even dynamic classes with no explicit bases are subtypes of object
@@ -569,7 +423,6 @@ produces slightly different error messages than assigned dynamic class creation:
```py
class Base: ...
# error: 6 [invalid-argument-type] "Argument to class `type` is incorrect: Expected `str`, found `Literal[b"Foo"]`"
type(b"Foo", (), {})
@@ -592,11 +445,9 @@ diagnostic about the unsupported base, rather than cascading errors:
```py
from ty_extensions import reveal_mro
class Base:
base_attr: int = 1
def f(x: type[Base]):
# error: [unsupported-dynamic-base] "Unsupported class base"
Child = type("Child", (x,), {})
@@ -618,7 +469,6 @@ MRO errors are detected and reported:
```py
class A: ...
# Duplicate bases are detected
# error: [duplicate-base] "Duplicate base class <class 'A'> in class `Dup`"
Dup = type("Dup", (A, A), {})
@@ -637,22 +487,14 @@ X = type("X", (Bar, Baz), {})
```py
class A: ...
class B(A): ...
class C(A): ...
# This creates an inconsistent MRO because D would need B before C (from first base)
# but also C before B (from second base inheritance through A)
class X(B, C): ...
class Y(C, B): ...
# error: [inconsistent-mro] "Cannot create a consistent method resolution order (MRO) for class `Conflict` with bases `[<class 'X'>, <class 'Y'>]`"
Conflict = type("Conflict", (X, Y), {})
```
@@ -663,156 +505,15 @@ Metaclass conflicts are detected and reported:
```py
class Meta1(type): ...
class Meta2(type): ...
class A(metaclass=Meta1): ...
class B(metaclass=Meta2): ...
# error: [conflicting-metaclass] "The metaclass of a derived class (`Bad`) must be a subclass of the metaclasses of all its bases, but `Meta1` (metaclass of base class `<class 'A'>`) and `Meta2` (metaclass of base class `<class 'B'>`) have no subclass relationship"
Bad = type("Bad", (A, B), {})
```
## `__slots__` in namespace dictionary
Dynamic classes can define `__slots__` in the namespace dictionary. Non-empty `__slots__` makes the
class a "disjoint base", which prevents it from being used alongside other disjoint bases in a class
hierarchy:
```py
# Dynamic class with non-empty __slots__
Slotted = type("Slotted", (), {"__slots__": ("x", "y")})
slotted = Slotted()
reveal_type(slotted) # revealed: Slotted
# Classes with empty __slots__ are not disjoint bases
EmptySlots = type("EmptySlots", (), {"__slots__": ()})
# Classes with no __slots__ are not disjoint bases
NoSlots = type("NoSlots", (), {})
# String __slots__ are treated as a single slot (non-empty)
StringSlots = type("StringSlots", (), {"__slots__": "x"})
```
Dynamic classes with non-empty `__slots__` cannot coexist with other disjoint bases:
```py
class RegularSlotted:
__slots__ = ("a",)
DynSlotted = type("DynSlotted", (), {"__slots__": ("b",)})
# error: [instance-layout-conflict]
class Conflict(
RegularSlotted,
DynSlotted,
): ...
```
Two dynamic classes with non-empty `__slots__` also conflict:
```py
A = type("A", (), {"__slots__": ("x",)})
B = type("B", (), {"__slots__": ("y",)})
# error: [instance-layout-conflict]
class Conflict(
A,
B,
): ...
```
`instance-layout-conflict` errors are also emitted for classes that inherit from dynamic classes
with disjoint bases:
```py
from typing import Any
class DisjointBase1:
__slots__ = ("a",)
class DisjointBase2:
__slots__ = ("b",)
def f(ns: dict[str, Any]):
cls1 = type("cls1", (DisjointBase1,), ns)
cls2 = type("cls2", (DisjointBase2,), ns)
# error: [instance-layout-conflict]
cls3 = type("cls3", (cls1, cls2), {})
# error: [instance-layout-conflict]
class Cls4(cls1, cls2): ...
```
When the namespace dictionary is dynamic (not a literal), we can't determine if `__slots__` is
defined, so no diagnostic is emitted:
```py
from typing import Any
class SlottedBase:
__slots__ = ("a",)
def f(ns: dict[str, Any]):
# The namespace might or might not contain __slots__, so no error is emitted
Dynamic = type("Dynamic", (), ns)
# No error: we can't prove there's a conflict since ns might not have __slots__
class MaybeConflict(SlottedBase, Dynamic): ...
```
## `instance-layout-conflict` diagnostic snapshots
<!-- snapshot-diagnostics -->
When the bases are a tuple literal, the diagnostic includes annotations for each conflicting base:
```py
class A:
__slots__ = ("x",)
class B:
__slots__ = ("y",)
# error: [instance-layout-conflict]
X = type("X", (A, B), {})
```
When the bases are not a tuple literal (e.g., a variable), the diagnostic is emitted without
per-base annotations:
```py
class C:
__slots__ = ("x",)
class D:
__slots__ = ("y",)
bases: tuple[type[C], type[D]] = (C, D)
# error: [instance-layout-conflict]
Y = type("Y", bases, {})
```
## Cyclic functional class definitions
## Cyclic dynamic class definitions
Self-referential class definitions using `type()` are detected. The name being defined is referenced
in the bases tuple before it's available:
@@ -834,14 +535,13 @@ def make_class(name: str):
reveal_type(cls) # revealed: <class '<unknown>'>
return cls
def make_classes(name1: str, name2: str):
cls1 = type(name1, (), {})
cls2 = type(name2, (), {})
def inner(x: cls1): ...
# error: [invalid-argument-type] "Argument to function `inner` is incorrect: Expected `mdtest_snippet.<locals of function 'make_classes'>.<unknown> @ src/mdtest_snippet.py:8:12`, found `mdtest_snippet.<locals of function 'make_classes'>.<unknown> @ src/mdtest_snippet.py:9:12`"
# error: [invalid-argument-type] "Argument to function `inner` is incorrect: Expected `mdtest_snippet.<locals of function 'make_classes'>.<unknown> @ src/mdtest_snippet.py:8`, found `mdtest_snippet.<locals of function 'make_classes'>.<unknown> @ src/mdtest_snippet.py:9`"
inner(cls2())
```
@@ -867,13 +567,9 @@ any attribute access returns `Unknown`:
```py
from ty_extensions import reveal_mro
class Base1: ...
class Base2: ...
def make_class(bases: tuple[type, ...]):
# Class literal is created with Unknown base in MRO
cls = type("Cls", bases, {})
@@ -896,7 +592,6 @@ classes:
class Base:
attr: int = 1
bases = (Base,)
Cls = type("Cls", bases, {})
reveal_type(Cls) # revealed: <class 'Cls'>
@@ -912,15 +607,13 @@ Unpacking arguments with `*args` or `**kwargs`:
```py
from ty_extensions import reveal_mro
class Base: ...
# Unpacking a tuple for bases
bases_tuple = (Base,)
Cls1 = type("Cls1", (*bases_tuple,), {})
reveal_type(Cls1) # revealed: <class 'Cls1'>
reveal_mro(Cls1) # revealed: (<class 'Cls1'>, <class 'Base'>, <class 'object'>)
reveal_mro(Cls1) # revealed: (<class 'Cls1'>, @Todo(StarredExpression), <class 'object'>)
# Unpacking a dict for the namespace - the dict contents are not tracked anyway
namespace = {"attr": 1}
@@ -969,7 +662,6 @@ This will be fixed when we support all `type()` calls (including inline) via gen
```py
class Base: ...
# TODO: Should infer `<class 'T'>` instead of `type`
T: type = type("T", (), {})
reveal_type(T) # revealed: type
@@ -992,11 +684,9 @@ synthesized:
from typing import Protocol
from ty_extensions import reveal_mro
class MyProtocol(Protocol):
def method(self) -> int: ...
ProtoImpl = type("ProtoImpl", (MyProtocol,), {})
reveal_type(ProtoImpl) # revealed: <class 'ProtoImpl'>
reveal_mro(ProtoImpl) # revealed: (<class 'ProtoImpl'>, <class 'MyProtocol'>, typing.Protocol, typing.Generic, <class 'object'>)
@@ -1012,12 +702,10 @@ reveal_type(instance) # revealed: ProtoImpl
from typing_extensions import TypedDict
from ty_extensions import reveal_mro
class MyDict(TypedDict):
name: str
age: int
DictSubclass = type("DictSubclass", (MyDict,), {})
reveal_type(DictSubclass) # revealed: <class 'DictSubclass'>
reveal_mro(DictSubclass) # revealed: (<class 'DictSubclass'>, <class 'MyDict'>, typing.TypedDict, <class 'object'>)
@@ -1030,12 +718,10 @@ reveal_mro(DictSubclass) # revealed: (<class 'DictSubclass'>, <class 'MyDict'>,
from typing import NamedTuple
from ty_extensions import reveal_mro
class Point(NamedTuple):
x: int
y: int
Point3D = type("Point3D", (Point,), {})
reveal_type(Point3D) # revealed: <class 'Point3D'>
# fmt: off
@@ -1050,16 +736,13 @@ reveal_mro(Point3D) # revealed: (<class 'Point3D'>, <class 'Point'>, <class 'tu
# that type() cannot provide. This applies even to empty enums.
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
class EmptyEnum(Enum):
pass
# TODO: We should emit a diagnostic here - type() cannot create Enum subclasses
ExtendedColor = type("ExtendedColor", (Color,), {})
reveal_type(ExtendedColor) # revealed: <class 'ExtendedColor'>
@@ -1081,12 +764,10 @@ class Base:
super().__init_subclass__(**kwargs)
cls.config = required_arg
# Regular class definition - this works and passes the argument
class Child(Base, required_arg="value"):
pass
# The dynamically assigned attribute has Unknown in its type
reveal_type(Child.config) # revealed: Unknown | str
@@ -1123,10 +804,8 @@ When a base class has a custom metaclass, the dynamic class inherits that metacl
class MyMeta(type):
custom_attr: str = "meta"
class Base(metaclass=MyMeta): ...
# Dynamic class inherits the metaclass from Base
Dynamic = type("Dynamic", (Base,), {})
reveal_type(Dynamic) # revealed: <class 'Dynamic'>

File diff suppressed because one or more lines are too long

View File

@@ -28,25 +28,18 @@ python-version = "3.12"
from __future__ import annotations
from ty_extensions import reveal_mro
class A:
def a(self): ...
aa: int = 1
class B(A):
def b(self): ...
bb: int = 2
class C(B):
def c(self): ...
cc: int = 3
reveal_mro(C) # revealed: (<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>)
super(C, C()).a
@@ -75,24 +68,19 @@ synthesized `Protocol`s that cannot be upcast to, or interpreted as, a non-`obje
import types
from typing_extensions import Callable, TypeIs, Literal, NewType, TypedDict
def f(): ...
class Foo[T]:
def method(self): ...
@property
def some_property(self): ...
type Alias = int
class SomeTypedDict(TypedDict):
x: int
y: bytes
N = NewType("N", int)
# revealed: <super: <class 'object'>, FunctionType>
@@ -112,11 +100,9 @@ reveal_type(super(object, Foo.some_property))
# revealed: <super: <class 'object'>, int>
reveal_type(super(object, N(42)))
def g(x: object) -> TypeIs[list[object]]:
return isinstance(x, list)
def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
if hasattr(x, "bar"):
# revealed: <Protocol with members 'bar'>
@@ -138,7 +124,6 @@ def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
# revealed: <super: <class 'object'>, dict[Literal["x", "y"], int | bytes]>
reveal_type(super(object, y))
# The first argument to `super()` must be an actual class object;
# instances of `GenericAlias` are not accepted at runtime:
#
@@ -154,7 +139,6 @@ class Super:
def method(self) -> int:
return 42
class Sub(Super):
def method(self: Sub) -> int:
# revealed: <super: <class 'Sub'>, Sub>
@@ -177,13 +161,11 @@ python-version = "3.12"
```py
from __future__ import annotations
class A:
def __init__(self, a: int): ...
@classmethod
def f(cls): ...
class B(A):
def __init__(self, a: int):
reveal_type(super()) # revealed: <super: <class 'B'>, Self@__init__>
@@ -195,7 +177,6 @@ class B(A):
reveal_type(super()) # revealed: <super: <class 'B'>, type[Self@f]>
super().f()
super(B, B(42)).__init__(42)
super(B, B).f()
```
@@ -207,7 +188,6 @@ import enum
from typing import Any, Self, Never, Protocol, Callable
from ty_extensions import Intersection
class BuilderMeta(type):
def __new__(
cls: type[Any],
@@ -220,7 +200,6 @@ class BuilderMeta(type):
# revealed: Any
return reveal_type(s.__new__(cls, name, bases, dct))
class BuilderMeta2(type):
def __new__(
cls: type[BuilderMeta2],
@@ -232,7 +211,6 @@ class BuilderMeta2(type):
s = reveal_type(super())
return reveal_type(s.__new__(cls, name, bases, dct)) # revealed: BuilderMeta2
class Foo[T]:
x: T
@@ -287,14 +265,12 @@ class Foo[T]:
# revealed: Unknown
reveal_type(super())
return self
# TypeVar bounded by `type[Foo]` rather than `Foo`
# TODO: Should error on signature - `self` is annotated as a class type, not an instance type
def method11[S: type[Foo[int]]](self: S, other: S) -> S:
# Delegates to the bound to resolve the super type
reveal_type(super()) # revealed: <super: <class 'Foo'>, <class 'Foo[int]'>>
return self
# TypeVar bounded by `type[Foo]`, used in `type[T]` position
# TODO: Should error on signature - `cls` would be `type[type[Foo[int]]]`, a metaclass
# Delegates to `type[Unknown]` since `type[type[Foo[int]]]` can't be constructed
@@ -303,10 +279,8 @@ class Foo[T]:
reveal_type(super()) # revealed: <super: <class 'Foo'>, Unknown>
raise NotImplementedError
type Alias = Bar
class Bar:
def method(self: Alias):
# revealed: <super: <class 'Bar'>, Bar>
@@ -320,13 +294,11 @@ class Bar:
# revealed: <super: <class 'Bar'>, Bar>
reveal_type(super())
class P(Protocol):
def method(self: P):
# revealed: <super: <class 'P'>, P>
reveal_type(super())
class E(enum.Enum):
X = 1
@@ -346,10 +318,8 @@ a plain `super` instance and does not support name lookup via the MRO.
class A:
a: int = 42
class B(A): ...
reveal_type(super(B)) # revealed: super
# error: [unresolved-attribute] "Object of type `super` has no attribute `a`"
@@ -365,10 +335,8 @@ successfully.
class A:
a: int = 3
class B(A): ...
reveal_type(super(B, B()).a) # revealed: int
# error: [invalid-assignment] "Cannot assign to attribute `a` on type `<super: <class 'B'>, B>`"
super(B, B()).a = 3
@@ -385,7 +353,6 @@ member, it should effectively behave like a dynamic type.
class A:
a: int = 1
def f(x):
reveal_type(x) # revealed: Unknown
@@ -403,7 +370,6 @@ def f(x):
```py
from __future__ import annotations
class A:
def test(self):
reveal_type(super()) # revealed: <super: <class 'A'>, Self@test>
@@ -418,7 +384,6 @@ class A:
def inner(t: C):
reveal_type(super()) # revealed: <super: <class 'B'>, C>
lambda x: reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
```
@@ -434,11 +399,9 @@ reveal_type(super(int, 3)) # revealed: <super: <class 'int'>, int>
reveal_type(super(str, "")) # revealed: <super: <class 'str'>, str>
reveal_type(super(bytes, b"")) # revealed: <super: <class 'bytes'>, bytes>
class E(Enum):
X = 42
reveal_type(super(E, E.X)) # revealed: <super: <class 'E'>, E>
```
@@ -461,10 +424,8 @@ class A:
@classmethod
def a2(cls): ...
class B(A): ...
# A.__dict__["a1"].__get__(B(), B)
reveal_type(super(B, B()).a1) # revealed: bound method B.a1() -> Unknown
# A.__dict__["a2"].__get__(B(), B)
@@ -484,20 +445,14 @@ super objects are combined into a union.
```py
from ty_extensions import reveal_mro
class A: ...
class B:
b: int = 42
class C(A, B): ...
class D(B, A): ...
def f(x: C | D):
reveal_mro(C) # revealed: (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>)
reveal_mro(D) # revealed: (<class 'D'>, <class 'B'>, <class 'A'>, <class 'object'>)
@@ -508,13 +463,11 @@ def f(x: C | D):
# error: [possibly-missing-attribute] "Attribute `b` may be missing on object of type `<super: <class 'A'>, C> | <super: <class 'A'>, D>`"
s.b
def f(flag: bool):
x = str() if flag else str("hello")
reveal_type(x) # revealed: Literal["", "hello"]
reveal_type(super(str, x)) # revealed: <super: <class 'str'>, str>
def f(x: int | str):
# error: [invalid-super-argument] "`str` is not an instance or subclass of `<class 'int'>` in `super(<class 'int'>, str)` call"
super(int, x)
@@ -526,7 +479,6 @@ in all cases.
```py
def f(flag: bool):
if flag:
class A:
x = 1
y: int = 1
@@ -534,16 +486,13 @@ def f(flag: bool):
a: str = "hello"
class B(A): ...
s = super(B, B())
else:
class C:
x = 2
y: int | str = "test"
class D(C): ...
s = super(D, D())
reveal_type(s) # revealed: <super: <class 'B'>, B> | <super: <class 'D'>, D>
@@ -565,12 +514,10 @@ python-version = "3.12"
```py
from ty_extensions import TypeOf, static_assert, is_subtype_of
class A[T]:
def f(self, a: T) -> T:
return a
class B[T](A[T]):
def f(self, a: T) -> T:
return super().f(a)
@@ -588,12 +535,10 @@ from __future__ import annotations
# error: [unavailable-implicit-super-arguments] "Cannot determine implicit arguments for 'super()' in this context"
reveal_type(super()) # revealed: Unknown
def f():
# error: [unavailable-implicit-super-arguments] "Cannot determine implicit arguments for 'super()' in this context"
super()
# No first argument in its scope
class A:
# error: [unavailable-implicit-super-arguments] "Cannot determine implicit arguments for 'super()' in this context"
@@ -603,7 +548,6 @@ class A:
def g():
# error: [unavailable-implicit-super-arguments] "Cannot determine implicit arguments for 'super()' in this context"
super()
# error: [unavailable-implicit-super-arguments] "Cannot determine implicit arguments for 'super()' in this context"
lambda: super()
@@ -631,7 +575,6 @@ runtime.
import typing
import collections
def f(x: int):
# error: [invalid-super-argument] "`int` is not a valid class"
super(x, x)
@@ -640,7 +583,6 @@ def f(x: int):
# error: [invalid-super-argument] "`TypeAliasType` is not a valid class"
super(IntAlias, 0)
# error: [invalid-super-argument] "`str` is not an instance or subclass of `<class 'int'>` in `super(<class 'int'>, str)` call"
# revealed: Unknown
reveal_type(super(int, str()))
@@ -649,13 +591,9 @@ reveal_type(super(int, str()))
# revealed: Unknown
reveal_type(super(int, str))
class A: ...
class B(A): ...
# error: [invalid-super-argument] "`A` is not an instance or subclass of `<class 'B'>` in `super(<class 'B'>, A)` call"
# revealed: Unknown
reveal_type(super(B, A()))
@@ -686,7 +624,6 @@ reveal_type(super(typing.ChainMap, collections.ChainMap())) # revealed: Unknown
# revealed: <super: <special-form 'typing.Generic'>, <class 'SupportsInt'>>
reveal_type(super(typing.Generic, typing.SupportsInt))
def _(x: type[typing.Any], y: typing.Any):
reveal_type(super(x, y)) # revealed: <super: Any, Any>
```
@@ -699,16 +636,11 @@ def _(x: type[typing.Any], y: typing.Any):
def coinflip() -> bool:
return False
def f():
if coinflip():
class A: ...
else:
class A: ...
super(A, A()) # error: [invalid-super-argument]
```
@@ -719,19 +651,16 @@ Accessing instance members through `super()` is not allowed.
```py
from __future__ import annotations
class A:
def __init__(self, a: int):
self.a = a
class B(A):
def __init__(self, a: int):
super().__init__(a)
# error: [unresolved-attribute] "Object of type `<super: <class 'B'>, Self@__init__>` has no attribute `a`"
super().a
# error: [unresolved-attribute] "Object of type `<super: <class 'B'>, B>` has no attribute `a`"
super(B, B(42)).a
```
@@ -747,10 +676,8 @@ class A:
def __getitem__(self, key: int) -> int:
return 42
class B(A): ...
reveal_type(A()[0]) # revealed: int
reveal_type(super(B, B()).__getitem__) # revealed: bound method B.__getitem__(key: int) -> int
# error: [not-subscriptable] "Cannot subscript object of type `<super: <class 'B'>, B>` with no `__getitem__` method"
@@ -773,26 +700,21 @@ from __future__ import annotations
from collections.abc import Mapping
from typing import Self
class Parent:
def __init__(self, children: Mapping[str, Self] | None = None) -> None:
self.children = children
class Child(Parent):
def __init__(self, children: Mapping[str, Child] | None = None) -> None:
# error: [invalid-argument-type] "Argument to bound method `__init__` is incorrect: Expected `Mapping[str, Self@__init__] | None`, found `Mapping[str, Child] | None`"
super().__init__(children)
# The fix is to use `Self` consistently in the subclass:
class Parent2:
def __init__(self, children: Mapping[str, Self] | None = None) -> None:
self.children = children
class Child2(Parent2):
def __init__(self, children: Mapping[str, Self] | None = None) -> None:
super().__init__(children) # OK
@@ -808,7 +730,6 @@ from typing import Protocol, Generic, TypeVar
_T_co = TypeVar("_T_co", covariant=True)
class MyProtocol(Protocol, Generic[_T_co]):
def __class_getitem__(cls, item):
# Accessing parent's __class_getitem__ through super()

View File

@@ -17,42 +17,11 @@ from ty_extensions import reveal_mro
A = int
class G[T]: ...
class C(A, G["B"]): ...
A = str
B = bytes
reveal_mro(C) # revealed: (<class 'C'>, <class 'int'>, <class 'G[bytes]'>, typing.Generic, <class 'object'>)
```
## Starred bases
These are currently not supported, but ideally we would support them in some limited situations.
```py
from ty_extensions import reveal_mro
class A: ...
class B: ...
class C: ...
bases = (A, B, C)
class Foo(*bases): ...
# revealed: (<class 'Foo'>, @Todo(StarredExpression), <class 'object'>)
reveal_mro(Foo)
```

View File

@@ -3,12 +3,10 @@
```py
from enum import Enum
class Answer(Enum):
NO = 0
YES = 1
reveal_type(Answer.NO == Answer.NO) # revealed: Literal[True]
reveal_type(Answer.NO == Answer.YES) # revealed: Literal[False]

View File

@@ -3,7 +3,6 @@
```py
class A: ...
def _(a1: A, a2: A, o: object):
n1 = None
n2 = None

View File

@@ -19,7 +19,6 @@ class A:
def __contains__(self, item: str) -> bool:
return True
reveal_type("hello" in A()) # revealed: bool
reveal_type("hello" not in A()) # revealed: bool
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[42]` and `A`"
@@ -38,12 +37,10 @@ class StringIterator:
def __next__(self) -> str:
return "foo"
class A:
def __iter__(self) -> StringIterator:
return StringIterator()
reveal_type("hello" in A()) # revealed: bool
reveal_type("hello" not in A()) # revealed: bool
reveal_type(42 in A()) # revealed: bool
@@ -62,7 +59,6 @@ class A:
def __getitem__(self, key: int) -> str:
return "foo"
reveal_type("hello" in A()) # revealed: bool
reveal_type("hello" not in A()) # revealed: bool
reveal_type(42 in A()) # revealed: bool
@@ -79,7 +75,6 @@ class A:
def __contains__(self, item: str) -> str:
return "foo"
reveal_type("hello" in A()) # revealed: bool
reveal_type("hello" not in A()) # revealed: bool
```
@@ -91,17 +86,14 @@ reveal_type("hello" not in A()) # revealed: bool
```py
from typing import Literal
class AlwaysTrue:
def __contains__(self, item: int) -> Literal[1]:
return 1
class AlwaysFalse:
def __contains__(self, item: int) -> Literal[""]:
return ""
reveal_type(42 in AlwaysTrue()) # revealed: Literal[True]
reveal_type(42 not in AlwaysTrue()) # revealed: Literal[False]
@@ -116,19 +108,13 @@ doesn't result in a fallback to `__iter__` or `__getitem__`:
```py
class CheckContains: ...
class CheckIter: ...
class CheckGetItem: ...
class CheckIterIterator:
def __next__(self) -> CheckIter:
return CheckIter()
class A:
def __contains__(self, item: CheckContains) -> bool:
return True
@@ -139,7 +125,6 @@ class A:
def __getitem__(self, key: int) -> CheckGetItem:
return CheckGetItem()
reveal_type(CheckContains() in A()) # revealed: bool
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `CheckIter` and `A`"
@@ -147,7 +132,6 @@ reveal_type(CheckIter() in A()) # revealed: bool
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `CheckGetItem` and `A`"
reveal_type(CheckGetItem() in A()) # revealed: bool
class B:
def __iter__(self) -> CheckIterIterator:
return CheckIterIterator()
@@ -155,7 +139,6 @@ class B:
def __getitem__(self, key: int) -> CheckGetItem:
return CheckGetItem()
reveal_type(CheckIter() in B()) # revealed: bool
# Always use `__iter__`, regardless of iterated type; there's no NotImplemented
# in this case, so there's no fallback to `__getitem__`
@@ -172,7 +155,6 @@ class A:
def __getitem__(self, key: str) -> str:
return "foo"
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal[42]` and `A`"
reveal_type(42 in A()) # revealed: bool
# error: [unsupported-operator] "Operator `in` is not supported between objects of type `Literal["hello"]` and `A`"
@@ -211,12 +193,10 @@ It may also be more appropriate to use `unsupported-operator` as the error code.
class NotBoolable:
__bool__: int = 3
class WithContains:
def __contains__(self, item) -> NotBoolable:
return NotBoolable()
# error: [unsupported-bool-conversion]
10 in WithContains()
# error: [unsupported-bool-conversion]

View File

@@ -16,25 +16,13 @@ most common case involves implementing these methods for the same type:
```py
from __future__ import annotations
class EqReturnType: ...
class NeReturnType: ...
class LtReturnType: ...
class LeReturnType: ...
class GtReturnType: ...
class GeReturnType: ...
class A:
def __eq__(self, other: A) -> EqReturnType: # error: [invalid-method-override]
return EqReturnType()
@@ -54,7 +42,6 @@ class A:
def __ge__(self, other: A) -> GeReturnType:
return GeReturnType()
reveal_type(A() == A()) # revealed: EqReturnType
reveal_type(A() != A()) # revealed: NeReturnType
reveal_type(A() < A()) # revealed: LtReturnType
@@ -71,25 +58,13 @@ type:
```py
from __future__ import annotations
class EqReturnType: ...
class NeReturnType: ...
class LtReturnType: ...
class LeReturnType: ...
class GtReturnType: ...
class GeReturnType: ...
class A:
def __eq__(self, other: B) -> EqReturnType: # error: [invalid-method-override]
return EqReturnType()
@@ -109,10 +84,8 @@ class A:
def __ge__(self, other: B) -> GeReturnType:
return GeReturnType()
class B: ...
reveal_type(A() == B()) # revealed: EqReturnType
reveal_type(A() != B()) # revealed: NeReturnType
reveal_type(A() < B()) # revealed: LtReturnType
@@ -130,25 +103,13 @@ these methods will be ignored here because they require a mismatched operand typ
```py
from __future__ import annotations
class EqReturnType: ...
class NeReturnType: ...
class LtReturnType: ...
class LeReturnType: ...
class GtReturnType: ...
class GeReturnType: ...
class A:
def __eq__(self, other: B) -> EqReturnType: # error: [invalid-method-override]
return EqReturnType()
@@ -168,10 +129,8 @@ class A:
def __ge__(self, other: B) -> GeReturnType:
return GeReturnType()
class Unrelated: ...
class B:
def __eq__(self, other: Unrelated) -> B: # error: [invalid-method-override]
return B()
@@ -179,7 +138,6 @@ class B:
def __ne__(self, other: Unrelated) -> B: # error: [invalid-method-override]
return B()
# Because `object.__eq__` and `object.__ne__` accept `object` in typeshed,
# this can only happen with an invalid override of these methods,
# but we still support it.
@@ -192,7 +150,6 @@ reveal_type(B() <= A()) # revealed: GeReturnType
reveal_type(B() > A()) # revealed: LtReturnType
reveal_type(B() >= A()) # revealed: LeReturnType
class C:
def __gt__(self, other: C) -> EqReturnType:
return EqReturnType()
@@ -200,7 +157,6 @@ class C:
def __ge__(self, other: C) -> NeReturnType:
return NeReturnType()
reveal_type(C() < C()) # revealed: EqReturnType
reveal_type(C() <= C()) # revealed: NeReturnType
```
@@ -214,25 +170,13 @@ than `A`.
```py
from __future__ import annotations
class EqReturnType: ...
class NeReturnType: ...
class LtReturnType: ...
class LeReturnType: ...
class GtReturnType: ...
class GeReturnType: ...
class A:
def __eq__(self, other: A) -> A: # error: [invalid-method-override]
return A()
@@ -252,7 +196,6 @@ class A:
def __ge__(self, other: A) -> A:
return A()
class B(A):
def __eq__(self, other: A) -> EqReturnType: # error: [invalid-method-override]
return EqReturnType()
@@ -272,7 +215,6 @@ class B(A):
def __ge__(self, other: A) -> GeReturnType: # error: [invalid-method-override]
return GeReturnType()
reveal_type(A() == B()) # revealed: EqReturnType
reveal_type(A() != B()) # revealed: NeReturnType
@@ -291,7 +233,6 @@ method has an mismatched type to operand, the comparison will fall back to the l
```py
from __future__ import annotations
class A:
def __lt__(self, other: A) -> A:
return A()
@@ -299,7 +240,6 @@ class A:
def __gt__(self, other: A) -> A:
return A()
class B(A):
def __lt__(self, other: int) -> B: # error: [invalid-method-override]
return B()
@@ -307,7 +247,6 @@ class B(A):
def __gt__(self, other: int) -> B: # error: [invalid-method-override]
return B()
reveal_type(A() < B()) # revealed: A
reveal_type(A() > B()) # revealed: A
```
@@ -329,15 +268,12 @@ from does_not_exist import Foo # error: [unresolved-import]
reveal_type(Foo) # revealed: Unknown
class X:
def __lt__(self, other: object) -> int:
return 42
class Y(Foo): ...
# TODO: Should be `int | Unknown`; see above discussion.
reveal_type(X() < Y()) # revealed: int
```
@@ -352,7 +288,6 @@ Please refer to the [docs](https://docs.python.org/3/reference/datamodel.html#ob
```py
from __future__ import annotations
class A:
def __eq__(self, other: int) -> A: # error: [invalid-method-override]
return A()
@@ -360,7 +295,6 @@ class A:
def __ne__(self, other: int) -> A: # error: [invalid-method-override]
return A()
reveal_type(A() == A()) # revealed: bool
reveal_type(A() != A()) # revealed: bool
```
@@ -370,7 +304,6 @@ reveal_type(A() != A()) # revealed: bool
```py
class A: ...
reveal_type(A() == object()) # revealed: bool
reveal_type(A() != object()) # revealed: bool
reveal_type(object() == A()) # revealed: bool
@@ -403,7 +336,6 @@ reveal_type(1 > 2j) # revealed: Unknown
# error: [unsupported-operator] "Operator `>=` is not supported between objects of type `Literal[1]` and `complex`"
reveal_type(1 >= 2j) # revealed: Unknown
def f(x: bool, y: int):
reveal_type(x < y) # revealed: bool
reveal_type(y < x) # revealed: bool
@@ -422,7 +354,6 @@ element) of a chained comparison.
class NotBoolable:
__bool__: int = 3
class Comparable:
def __lt__(self, item) -> NotBoolable:
return NotBoolable()
@@ -430,7 +361,6 @@ class Comparable:
def __gt__(self, item) -> NotBoolable:
return NotBoolable()
# error: [unsupported-bool-conversion]
10 < Comparable() < 20
# error: [unsupported-bool-conversion]
@@ -444,17 +374,14 @@ Comparable() < Comparable() # fine
```py
from typing import Literal
class AlwaysTrue:
def __call__(self, other: object) -> Literal[True]:
return True
class A:
__eq__: AlwaysTrue = AlwaysTrue()
__lt__: AlwaysTrue = AlwaysTrue()
reveal_type(A() == A()) # revealed: Literal[True]
reveal_type(A() < A()) # revealed: Literal[True]
reveal_type(A() > A()) # revealed: Literal[True]

View File

@@ -8,20 +8,16 @@ types, we can infer that the result for the intersection type is also true/false
```py
from typing import Literal
class Base:
def __gt__(self, other) -> bool:
return False
class Child1(Base):
def __eq__(self, other) -> Literal[True]:
return True
class Child2(Base): ...
def _(x: Base):
c1 = Child1()
@@ -99,7 +95,6 @@ def _(x: int):
```py
class A: ...
def _(o: object):
a = A()
n = None
@@ -121,11 +116,8 @@ intersection type:
```py
class NonContainer1: ...
class NonContainer2: ...
def _(x: object):
if isinstance(x, NonContainer1):
if isinstance(x, NonContainer2):
@@ -143,7 +135,6 @@ class Container:
def __contains__(self, x) -> bool:
return False
def _(x: object):
if isinstance(x, NonContainer1):
if isinstance(x, Container):
@@ -176,10 +167,8 @@ class Container:
def __contains__(self, x) -> bool:
return False
class NonContainer: ...
def _(x: object):
if isinstance(x, Container):
if not isinstance(x, NonContainer):

View File

@@ -21,7 +21,6 @@ Walking through examples:
```py
from __future__ import annotations
class A:
def __lt__(self, other) -> A:
return self
@@ -29,17 +28,14 @@ class A:
def __gt__(self, other) -> bool:
return False
class B:
def __lt__(self, other) -> B:
return self
class C:
def __lt__(self, other) -> C:
return self
x = A() < B() < C()
reveal_type(x) # revealed: (A & ~AlwaysTruthy) | B

View File

@@ -152,25 +152,13 @@ of the dunder methods.)
```py
from __future__ import annotations
class EqReturnType: ...
class NeReturnType: ...
class LtReturnType: ...
class LeReturnType: ...
class GtReturnType: ...
class GeReturnType: ...
class A:
def __eq__(self, o: object) -> EqReturnType: # error: [invalid-method-override]
return EqReturnType()
@@ -190,7 +178,6 @@ class A:
def __ge__(self, o: A) -> GeReturnType:
return GeReturnType()
a = (A(), A())
reveal_type(a == a) # revealed: bool
@@ -211,15 +198,12 @@ reveal_type(b <= c) # revealed: Literal[True]
reveal_type(b > c) # revealed: Literal[False]
reveal_type(b >= c) # revealed: Literal[False]
class LtReturnTypeOnB: ...
class B:
def __lt__(self, o: B) -> LtReturnTypeOnB:
return LtReturnTypeOnB()
reveal_type((A(), B()) < (A(), B())) # revealed: LtReturnType | LtReturnTypeOnB | Literal[False]
```
@@ -278,7 +262,6 @@ comparison can clearly conclude before encountering an error, the error should n
```py
def _(n: int, s: str):
class A: ...
# error: [unsupported-operator] "Operator `<` is not supported between two objects of type `A`"
A() < A()
# error: [unsupported-operator] "Operator `<=` is not supported between two objects of type `A`"
@@ -483,7 +466,6 @@ def compute_chained_comparison():
class NotBoolable:
__bool__: int = 5
class Comparable:
def __lt__(self, other) -> NotBoolable:
return NotBoolable()
@@ -491,7 +473,6 @@ class Comparable:
def __gt__(self, other) -> NotBoolable:
return NotBoolable()
a = (1, Comparable())
b = (1, Comparable())
@@ -513,13 +494,11 @@ pair of elements at equivalent positions cannot be converted to a `bool`:
class NotBoolable:
__bool__: None = None
class A:
# error: [invalid-method-override]
def __eq__(self, other) -> NotBoolable:
return NotBoolable()
# error: [unsupported-bool-conversion]
(A(),) == (A(),)
```
@@ -530,11 +509,9 @@ class A:
from __future__ import annotations
from typing import NamedTuple
class Node(NamedTuple):
parent: Node | None
def _(n: Node):
reveal_type(n.parent is n) # revealed: bool
```

View File

@@ -75,7 +75,6 @@ back to `bool` for the result type instead of trying to infer something more pre
```py
from typing import Literal
def _(
x: list[int] | Literal[1],
y: list[int] | Literal[1],

View File

@@ -5,7 +5,6 @@
```py
def _(flag: bool, flag1: bool, flag2: bool):
class A: ...
a = 1 in 7 # error: "Operator `in` is not supported between objects of type `Literal[1]` and `Literal[7]`"
reveal_type(a) # revealed: bool

View File

@@ -6,17 +6,14 @@
# revealed: int
[reveal_type(x) for x in range(3)]
class Row:
def __next__(self) -> range:
return range(3)
class Table:
def __iter__(self) -> Row:
return Row()
# revealed: tuple[int, range]
[reveal_type((cell, row)) for row in Table() for cell in row]
@@ -41,12 +38,10 @@ class Row:
def __next__(self) -> range:
return range(3)
class Table:
def __iter__(self) -> Row:
return Row()
# revealed: tuple[int, range]
[[reveal_type((cell, row)) for cell in row] for row in Table()]
```
@@ -90,7 +85,6 @@ Starred expressions must be iterable
```py
class NotIterable: ...
# This is fine:
x = [*range(3)]
@@ -107,12 +101,10 @@ class AsyncIterator:
async def __anext__(self) -> int:
return 42
class AsyncIterable:
def __aiter__(self) -> AsyncIterator:
return AsyncIterator()
async def _():
# revealed: int
[reveal_type(x) async for x in AsyncIterable()]
@@ -188,7 +180,6 @@ The type context is propagated down into the comprehension:
class Person(TypedDict):
name: str
# TODO: This should not error.
# error: [invalid-assignment]
persons: list[Person] = [{"name": n} for n in ["Alice", "Bob"]]

View File

@@ -42,7 +42,6 @@ def _(flag: bool):
class NotBoolable:
__bool__: int = 3
# error: [unsupported-bool-conversion] "Boolean conversion is not supported for type `NotBoolable`"
3 if NotBoolable() else 4
```

View File

@@ -122,7 +122,6 @@ def _(flag: bool, flag2: bool):
def check(x: int) -> bool:
return bool(x)
if check(x := 1):
x = 2
elif check(x := 3):
@@ -137,7 +136,6 @@ reveal_type(x) # revealed: Literal[2, 3, 4]
def check(x) -> bool:
return bool(x)
def _(flag: bool):
x = 1 if flag else None
y = 0
@@ -156,7 +154,6 @@ def _(flag: bool):
class NotBoolable:
__bool__: int = 3
# error: [unsupported-bool-conversion] "Boolean conversion is not supported for type `NotBoolable`"
if NotBoolable():
...

View File

@@ -63,12 +63,10 @@ This leads us to infer `Literal[1, 3]` as the type of `y` after the `match` stat
```py
from typing import final
@final
class C:
pass
def _(subject: C):
y = 1
match subject:
@@ -87,24 +85,19 @@ all subpatterns in the class pattern match.
```py
from typing import final
class Foo:
pass
class FooSub(Foo):
pass
class Bar:
pass
@final
class Baz:
pass
def _(target: FooSub):
y = 1
@@ -118,7 +111,6 @@ def _(target: FooSub):
reveal_type(y) # revealed: Literal[3]
def _(target: FooSub):
y = 1
@@ -132,7 +124,6 @@ def _(target: FooSub):
reveal_type(y) # revealed: Literal[3, 4]
def _(target: FooSub | str):
y = 1
@@ -153,16 +144,13 @@ def _(target: FooSub | str):
from typing_extensions import assert_never
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
class Other: ...
def _(target: Point):
y = 1
@@ -176,7 +164,6 @@ def _(target: Point):
reveal_type(y) # revealed: Literal[1, 2, 3, 4]
def _(target: Point):
match target:
case Point(x, y): # irrefutable sub-patterns
@@ -184,7 +171,6 @@ def _(target: Point):
case _:
assert_never(target)
def _(target: Point | Other):
match target:
case Point(0, 0):
@@ -204,7 +190,6 @@ Singleton patterns are matched based on identity, not equality comparisons or `i
```py
from typing import Literal
def _(target: Literal[True, False]):
y = 1
@@ -218,7 +203,6 @@ def _(target: Literal[True, False]):
reveal_type(y) # revealed: Literal[2, 3]
def _(target: bool):
y = 1
@@ -232,7 +216,6 @@ def _(target: bool):
reveal_type(y) # revealed: Literal[2, 3]
def _(target: None):
y = 1
@@ -246,7 +229,6 @@ def _(target: None):
reveal_type(y) # revealed: Literal[4]
def _(target: None | Literal[True]):
y = 1
@@ -260,7 +242,6 @@ def _(target: None | Literal[True]):
reveal_type(y) # revealed: Literal[2, 4]
# bool is an int subclass
def _(target: int):
y = 1
@@ -275,7 +256,6 @@ def _(target: int):
reveal_type(y) # revealed: Literal[1, 2, 3]
def _(target: str):
y = 1
@@ -295,12 +275,10 @@ def _(target: str):
```py
from enum import Enum
class Answer(Enum):
NO = 0
YES = 1
def _(answer: Answer):
y = 0
match answer:
@@ -321,7 +299,6 @@ A `|` pattern matches if any of the subpatterns match.
```py
from typing import Literal, final
def _(target: Literal["foo", "baz"]):
y = 1
@@ -333,7 +310,6 @@ def _(target: Literal["foo", "baz"]):
reveal_type(y) # revealed: Literal[2, 3]
def _(target: None):
y = 1
@@ -345,12 +321,10 @@ def _(target: None):
reveal_type(y) # revealed: Literal[2]
@final
class Baz:
pass
def _(target: int | None | float):
y = 1
@@ -362,10 +336,8 @@ def _(target: int | None | float):
reveal_type(y) # revealed: Literal[1, 2]
class Foo: ...
def _(target: None | Foo):
y = 1
@@ -403,7 +375,6 @@ def _(target: int | str):
class NotBoolable:
__bool__: int = 3
def _(target: int, flag: NotBoolable):
y = 1
match target:
@@ -424,12 +395,10 @@ is not covered by any case, even when all enum members are covered.
```py
from enum import Enum
class Answer(Enum):
YES = 1
NO = 2
def _(answer: Answer | None):
y = 0
match answer:
@@ -442,7 +411,6 @@ def _(answer: Answer | None):
# so y could still be 0
reveal_type(y) # revealed: Literal[0, 1, 2]
def _(answer: Answer | None):
match answer:
case Answer.YES:
@@ -454,10 +422,8 @@ def _(answer: Answer | None):
reveal_type(answer) # revealed: None
return 3
class Foo: ...
def _(answer: Answer | None):
match answer:
case Answer.YES:

View File

@@ -7,12 +7,10 @@ Deferred annotations can result in cycles in resolving a function signature:
```py
from __future__ import annotations
# error: [invalid-type-form]
def f(x: f):
pass
reveal_type(f) # revealed: def f(x: Unknown) -> Unknown
```
@@ -29,7 +27,6 @@ class Point:
def replace_with(self, other: "Point") -> None:
self.x, self.y = other.x, other.y
p = Point()
reveal_type(p.x) # revealed: Unknown | int
reveal_type(p.y) # revealed: Unknown | int
@@ -47,18 +44,15 @@ from typing import Union, TypeAliasType, Sequence, Mapping
A = list["A" | None]
def f(x: A):
# TODO: should be `list[A | None]`?
reveal_type(x) # revealed: list[Divergent]
# TODO: should be `A | None`?
reveal_type(x[0]) # revealed: Divergent
JSONPrimitive = Union[str, int, float, bool, None]
JSONValue = TypeAliasType("JSONValue", 'Union[JSONPrimitive, Sequence["JSONValue"], Mapping[str, "JSONValue"]]')
def _(x: JSONValue):
# TODO: should be `JSONValue`
reveal_type(x) # revealed: Divergent
@@ -71,7 +65,6 @@ from typing import Generic, TypeVar
B = TypeVar("B", bound="Base")
class Base(Generic[B]):
pass
```
@@ -93,28 +86,24 @@ class C:
def f(self: "C"):
def inner_a(positional=self.a):
return
self.a = inner_a
# revealed: def inner_a(positional=...) -> Unknown
reveal_type(inner_a)
def inner_b(*, kw_only=self.b):
return
self.b = inner_b
# revealed: def inner_b(*, kw_only=...) -> Unknown
reveal_type(inner_b)
def inner_c(positional_only=self.c, /):
return
self.c = inner_c
# revealed: def inner_c(positional_only=..., /) -> Unknown
reveal_type(inner_c)
def inner_d(*, kw_only=self.d):
return
self.d = inner_d
# revealed: def inner_d(*, kw_only=...) -> Unknown
reveal_type(inner_d)
@@ -127,7 +116,6 @@ class D:
def f(self: "D"):
# error: [invalid-parameter-default] "Default value of type `Unknown | (def inner_a(a: int = ...) -> Unknown)` is not assignable to annotated parameter type `int`"
def inner_a(a: int = self.a): ...
self.a = inner_a
```
@@ -165,7 +153,6 @@ class Cyclic:
if isinstance(self.data, str):
self.data = {"url": self.data}
# revealed: Unknown | str | dict[Unknown, Unknown] | dict[Unknown | str, Unknown | str]
reveal_type(Cyclic("").data)
```

View File

@@ -13,19 +13,16 @@ class, or metaclass is a `dataclass`-like construct.
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
def my_dataclass[T](cls: type[T]) -> type[T]:
# modify cls
return cls
@my_dataclass
class Person:
name: str
age: int | None = None
Person("Alice", 20)
Person("Bob", None)
Person("Bob")
@@ -41,22 +38,18 @@ If we want our `dataclass`-like decorator to also take parameters, that is also
```py
from typing_extensions import dataclass_transform, Callable
@dataclass_transform()
def versioned_class[T](*, version: int = 1):
def decorator(cls):
# modify cls
return cls
return decorator
@versioned_class(version=2)
class Person:
name: str
age: int | None = None
Person("Alice", 20)
# error: [missing-argument]
@@ -68,7 +61,6 @@ We properly type-check the arguments to the decorator:
```py
from typing_extensions import dataclass_transform, Callable
# error: [invalid-argument-type]
@versioned_class(version="a string")
class C:
@@ -85,19 +77,16 @@ The examples from this section are straight from the Python documentation on
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
def create_model[T](cls: type[T]) -> type[T]:
...
return cls
@create_model
class CustomerModel:
id: int
name: str
CustomerModel(id=1, name="Test")
```
@@ -106,19 +95,15 @@ CustomerModel(id=1, name="Test")
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
class ModelMeta(type): ...
class ModelBase(metaclass=ModelMeta): ...
class CustomerModel(ModelBase):
id: int
name: str
CustomerModel(id=1, name="Test")
# error: [missing-argument]
@@ -130,16 +115,13 @@ CustomerModel()
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
class ModelBase: ...
class CustomerModel(ModelBase):
id: int
name: str
CustomerModel(id=1, name="Test")
```
@@ -158,67 +140,52 @@ This can be overwritten using the `order` argument to the custom decorator:
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
def normal(*, order: bool = False):
raise NotImplementedError
@dataclass_transform(order_default=False)
def order_default_false(*, order: bool = False):
raise NotImplementedError
@dataclass_transform(order_default=True)
def order_default_true(*, order: bool = True):
raise NotImplementedError
@normal
class Normal:
inner: int
Normal(1) < Normal(2) # error: [unsupported-operator]
@normal(order=True)
class NormalOverwritten:
inner: int
reveal_type(NormalOverwritten(1) < NormalOverwritten(2)) # revealed: bool
@order_default_false
class OrderFalse:
inner: int
OrderFalse(1) < OrderFalse(2) # error: [unsupported-operator]
@order_default_false(order=True)
class OrderFalseOverwritten:
inner: int
reveal_type(OrderFalseOverwritten(1) < OrderFalseOverwritten(2)) # revealed: bool
@order_default_true
class OrderTrue:
inner: int
reveal_type(OrderTrue(1) < OrderTrue(2)) # revealed: bool
@order_default_true(order=False)
class OrderTrueOverwritten:
inner: int
# error: [unsupported-operator]
OrderTrueOverwritten(1) < OrderTrueOverwritten(2)
```
@@ -229,14 +196,11 @@ This also works for metaclass-based transformers:
@dataclass_transform(order_default=True)
class OrderedModelMeta(type): ...
class OrderedModel(metaclass=OrderedModelMeta): ...
class TestWithMeta(OrderedModel):
inner: int
reveal_type(TestWithMeta(1) < TestWithMeta(2)) # revealed: bool
```
@@ -246,11 +210,9 @@ And for base-class-based transformers:
@dataclass_transform(order_default=True)
class OrderedModelBase: ...
class TestWithBase(OrderedModelBase):
inner: int
reveal_type(TestWithBase(1) < TestWithBase(2)) # revealed: bool
```
@@ -262,14 +224,12 @@ When provided, sets the default value for the `kw_only` parameter of `field()`.
from typing import dataclass_transform
from dataclasses import field
@dataclass_transform(kw_only_default=True)
def create_model(*, kw_only: bool = True): ...
@create_model()
class A:
name: str
a = A(name="Harry")
# error: [missing-argument]
# error: [too-many-positional-arguments]
@@ -284,7 +244,6 @@ class CustomerModel:
id: int
name: str
c = CustomerModel(1, "Harry")
```
@@ -294,14 +253,11 @@ This also works for metaclass-based transformers:
@dataclass_transform(kw_only_default=True)
class ModelMeta(type): ...
class ModelBase(metaclass=ModelMeta): ...
class TestMeta(ModelBase):
name: str
reveal_type(TestMeta.__init__) # revealed: (self: TestMeta, *, name: str) -> None
```
@@ -311,11 +267,9 @@ And for base-class-based transformers:
@dataclass_transform(kw_only_default=True)
class ModelBase: ...
class TestBase(ModelBase):
name: str
reveal_type(TestBase.__init__) # revealed: (self: TestBase, *, name: str) -> None
```
@@ -326,14 +280,12 @@ When provided, sets the default value for the `frozen` parameter of `field()`.
```py
from typing import dataclass_transform
@dataclass_transform(frozen_default=True)
def create_model(*, frozen: bool = True): ...
@create_model()
class ImmutableModel:
name: str
i = ImmutableModel(name="test")
i.name = "new" # error: [invalid-assignment]
```
@@ -345,7 +297,6 @@ Again, this can be overridden by setting `frozen=False` when applying the decora
class MutableModel:
name: str
m = MutableModel(name="test")
m.name = "new" # No error
```
@@ -356,14 +307,11 @@ This also works for metaclass-based transformers:
@dataclass_transform(frozen_default=True)
class ModelMeta(type): ...
class ModelBase(metaclass=ModelMeta): ...
class TestMeta(ModelBase):
name: str
t = TestMeta(name="test")
t.name = "new" # error: [invalid-assignment]
```
@@ -374,11 +322,9 @@ And for base-class-based transformers:
@dataclass_transform(frozen_default=True)
class ModelBase: ...
class TestMeta(ModelBase):
name: str
t = TestMeta(name="test")
t.name = "new" # error: [invalid-assignment]
@@ -391,7 +337,6 @@ Combining several of these parameters also works as expected:
```py
from typing import dataclass_transform
@dataclass_transform(eq_default=True, order_default=False, kw_only_default=True, frozen_default=True)
def create_model(*, eq: bool = True, order: bool = False, kw_only: bool = True, frozen: bool = True): ...
@create_model(eq=False, order=True, kw_only=False, frozen=False)
@@ -399,7 +344,6 @@ class OverridesAllParametersModel:
name: str
age: int
# Positional arguments are allowed:
model = OverridesAllParametersModel("test", 25)
@@ -421,25 +365,21 @@ from `order=False` (default) to `order=True`:
```py
from typing import dataclass_transform
@dataclass_transform(frozen_default=True)
def default_frozen_model(*, frozen: bool = True, order: bool = False): ...
@default_frozen_model()
class Frozen:
name: str
f = Frozen(name="test")
f.name = "new" # error: [invalid-assignment]
Frozen(name="A") < Frozen(name="B") # error: [unsupported-operator]
@default_frozen_model(frozen=False, order=True)
class Mutable:
name: str
m = Mutable(name="test")
m.name = "new" # No error
@@ -451,7 +391,6 @@ reveal_type(Mutable(name="A") < Mutable(name="B")) # revealed: bool
```py
from typing import dataclass_transform
@dataclass_transform(frozen_default=True)
class DefaultFrozenMeta(type):
def __new__(
@@ -464,24 +403,19 @@ class DefaultFrozenMeta(type):
order: bool = False,
): ...
class DefaultFrozenModel(metaclass=DefaultFrozenMeta): ...
class Frozen(DefaultFrozenModel):
name: str
f = Frozen(name="test")
f.name = "new" # error: [invalid-assignment]
Frozen(name="A") < Frozen(name="B") # error: [unsupported-operator]
class Mutable(DefaultFrozenModel, frozen=False, order=True):
name: str
m = Mutable(name="test")
# TODO: This should not be an error. In order to support this, we need to implement the precise `frozen` semantics of
# `dataclass_transform` described here: https://typing.python.org/en/latest/spec/dataclasses.html#dataclass-semantics
@@ -495,7 +429,6 @@ reveal_type(Mutable(name="A") < Mutable(name="B")) # revealed: bool
```py
from typing import dataclass_transform
@dataclass_transform(frozen_default=True)
class DefaultFrozenModel:
def __init_subclass__(
@@ -505,21 +438,17 @@ class DefaultFrozenModel:
order: bool = False,
): ...
class Frozen(DefaultFrozenModel):
name: str
f = Frozen(name="test")
f.name = "new" # error: [invalid-assignment]
Frozen(name="A") < Frozen(name="B") # error: [unsupported-operator]
class Mutable(DefaultFrozenModel, frozen=False, order=True):
name: str
m = Mutable(name="test")
m.name = "new" # No error
@@ -538,25 +467,20 @@ from typing_extensions import dataclass_transform, TypeVar, Callable
T = TypeVar("T", bound=type)
@dataclass_transform()
def fancy_model(*, slots: bool = False) -> Callable[[T], T]:
raise NotImplementedError
@fancy_model()
class NoSlots:
name: str
NoSlots.__slots__ # error: [unresolved-attribute]
@fancy_model(slots=True)
class WithSlots:
name: str
reveal_type(WithSlots.__slots__) # revealed: tuple[Literal["name"]]
```
@@ -565,29 +489,23 @@ reveal_type(WithSlots.__slots__) # revealed: tuple[Literal["name"]]
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
class FancyMeta(type):
def __new__(cls, name, bases, namespace, *, slots: bool = False):
...
return super().__new__(cls, name, bases, namespace)
class FancyBase(metaclass=FancyMeta): ...
class NoSlots(FancyBase):
name: str
# error: [unresolved-attribute]
NoSlots.__slots__
class WithSlots(FancyBase, slots=True):
name: str
reveal_type(WithSlots.__slots__) # revealed: tuple[Literal["name"]]
```
@@ -596,25 +514,20 @@ reveal_type(WithSlots.__slots__) # revealed: tuple[Literal["name"]]
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
class FancyBase:
def __init_subclass__(cls, *, slots: bool = False):
...
super().__init_subclass__()
class NoSlots(FancyBase):
name: str
NoSlots.__slots__ # error: [unresolved-attribute]
class WithSlots(FancyBase, slots=True):
name: str
reveal_type(WithSlots.__slots__) # revealed: tuple[Literal["name"]]
```
@@ -632,21 +545,18 @@ checkers do not seem to support this either.
```py
from typing_extensions import dataclass_transform, Any
def fancy_field(*, init: bool = True, kw_only: bool = False, alias: str | None = None) -> Any: ...
@dataclass_transform(field_specifiers=(fancy_field,))
def fancy_model[T](cls: type[T]) -> type[T]:
...
return cls
@fancy_model
class Person:
id: int = fancy_field(init=False)
internal_name: str = fancy_field(alias="name")
age: int | None = fancy_field(kw_only=True)
reveal_type(Person.__init__) # revealed: (self: Person, name: str, *, age: int | None) -> None
alice = Person("Alice", age=30)
@@ -661,7 +571,6 @@ reveal_type(alice.age) # revealed: int | None
```py
from typing_extensions import dataclass_transform, Any
def fancy_field(*, init: bool = True, kw_only: bool = False, alias: str | None = None) -> Any: ...
@dataclass_transform(field_specifiers=(fancy_field,))
class FancyMeta(type):
@@ -669,16 +578,13 @@ class FancyMeta(type):
...
return super().__new__(cls, name, bases, namespace)
class FancyBase(metaclass=FancyMeta): ...
class Person(FancyBase):
id: int = fancy_field(init=False)
internal_name: str = fancy_field(alias="name")
age: int | None = fancy_field(kw_only=True)
reveal_type(Person.__init__) # revealed: (self: Person, name: str, *, age: int | None) -> None
alice = Person("Alice", age=30)
@@ -693,7 +599,6 @@ reveal_type(alice.age) # revealed: int | None
```py
from typing_extensions import dataclass_transform, Any
def fancy_field(*, init: bool = True, kw_only: bool = False, alias: str | None = None) -> Any: ...
@dataclass_transform(field_specifiers=(fancy_field,))
class FancyBase:
@@ -701,13 +606,11 @@ class FancyBase:
...
super().__init_subclass__()
class Person(FancyBase):
id: int = fancy_field(init=False)
internal_name: str = fancy_field(alias="name")
age: int | None = fancy_field(kw_only=True)
reveal_type(Person.__init__) # revealed: (self: Person, name: str, *, age: int | None) -> None
alice = Person("Alice", age=30)
@@ -724,20 +627,17 @@ Field specifiers can have default arguments that should be respected:
```py
from typing_extensions import dataclass_transform, Any
def fancy_field(*, init: bool = False) -> Any: ...
@dataclass_transform(field_specifiers=(fancy_field,))
def fancy_model[T](cls: type[T]) -> type[T]:
...
return cls
@fancy_model
class Person:
id: int = fancy_field()
name: str = fancy_field(init=True)
reveal_type(Person.__init__) # revealed: (self: Person, name: str) -> None
Person(name="Alice")
@@ -755,13 +655,11 @@ correctly when passed via `**kwargs` for all three kinds of transformers.
from typing import Any
from typing_extensions import dataclass_transform
def field(**kwargs: Any) -> Any: ...
@dataclass_transform(field_specifiers=(field,))
def create_model[T](cls: type[T]) -> type[T]:
return cls
@create_model
class Person:
id: int = field(init=False)
@@ -771,7 +669,6 @@ class Person:
email: str = field(kw_only=True)
internal_notes: str = field(alias="notes")
# revealed: (self: Person, name: str, age: int = ..., tags: list[str] = ..., notes: str, *, email: str) -> None
reveal_type(Person.__init__)
@@ -785,15 +682,12 @@ Person("Bob", email="bob@example.com", notes="other notes")
from typing import Any
from typing_extensions import dataclass_transform
def field(**kwargs: Any) -> Any: ...
@dataclass_transform(field_specifiers=(field,))
class ModelMeta(type): ...
class ModelBase(metaclass=ModelMeta): ...
class Person(ModelBase):
id: int = field(init=False)
name: str
@@ -802,7 +696,6 @@ class Person(ModelBase):
email: str = field(kw_only=True)
internal_notes: str = field(alias="notes")
# revealed: (self: Person, name: str, age: int = ..., tags: list[str] = ..., notes: str, *, email: str) -> None
reveal_type(Person.__init__)
@@ -816,12 +709,10 @@ Person("Bob", email="bob@example.com", notes="other notes")
from typing import Any
from typing_extensions import dataclass_transform
def field(**kwargs: Any) -> Any: ...
@dataclass_transform(field_specifiers=(field,))
class ModelBase: ...
class Person(ModelBase):
id: int = field(init=False)
name: str
@@ -830,7 +721,6 @@ class Person(ModelBase):
email: str = field(kw_only=True)
internal_notes: str = field(alias="notes")
# revealed: (self: Person, name: str, age: int = ..., tags: list[str] = ..., notes: str, *, email: str) -> None
reveal_type(Person.__init__)
@@ -846,13 +736,11 @@ the synthesized `__init__` method.
```py
from typing_extensions import dataclass_transform, Any
def field_with_alias(*, alias: str | None = None, kw_only: bool = False) -> Any: ...
@dataclass_transform(field_specifiers=(field_with_alias,))
def model[T](cls: type[T]) -> type[T]:
return cls
@model
class Person:
internal_name: str = field_with_alias(alias="name")
@@ -897,7 +785,6 @@ p = Person(name="Alice", internal_age=30)
```py
from typing_extensions import dataclass_transform, overload, Any
@overload
def fancy_field(*, init: bool = True) -> Any: ...
@overload
@@ -908,14 +795,12 @@ def fancy_model[T](cls: type[T]) -> type[T]:
...
return cls
@fancy_model
class Person:
id: int = fancy_field(init=False)
name: str = fancy_field()
age: int | None = fancy_field(kw_only=True)
reveal_type(Person.__init__) # revealed: (self: Person, name: str, *, age: int | None) -> None
```
@@ -927,21 +812,18 @@ Make sure that models are only affected by the field specifiers of their own tra
from typing_extensions import dataclass_transform, Any
from dataclasses import field
def outer_field(*, init: bool = True, kw_only: bool = False) -> Any: ...
@dataclass_transform(field_specifiers=(outer_field,))
def outer_model[T](cls: type[T]) -> type[T]:
# ...
return cls
def inner_field(*, init: bool = True, kw_only: bool = False) -> Any: ...
@dataclass_transform(field_specifiers=(inner_field,))
def inner_model[T](cls: type[T]) -> type[T]:
# ...
return cls
@outer_model
class Outer:
@inner_model
@@ -952,7 +834,6 @@ class Outer:
outer_a: int = outer_field(init=False)
outer_b: str = inner_field(init=False)
reveal_type(Outer.__init__) # revealed: (self: Outer, outer_b: str = ...) -> None
reveal_type(Outer.Inner.__init__) # revealed: (self: Inner, inner_b: str = ...) -> None
```
@@ -969,7 +850,6 @@ from typing_extensions import dataclass_transform, TypeVar, Callable, overload
T = TypeVar("T", bound=type)
@overload
def versioned_class(
cls: T,
@@ -989,17 +869,14 @@ def versioned_class(
) -> T | Callable[[T], T]:
raise NotImplementedError
@versioned_class
class D1:
x: str
@versioned_class(version=2)
class D2:
x: str
D1("a")
D2("a")
@@ -1014,7 +891,6 @@ from typing_extensions import dataclass_transform, TypeVar, Callable, overload
T = TypeVar("T", bound=type)
@overload
@dataclass_transform()
def versioned_class(
@@ -1034,17 +910,14 @@ def versioned_class(
) -> T | Callable[[T], T]:
raise NotImplementedError
@versioned_class
class D1:
x: str
@versioned_class(version=2)
class D2:
x: str
D1("a")
D2("a")
@@ -1064,21 +937,17 @@ sure that we recognize all fields in a hierarchy like this:
from dataclasses import dataclass
from typing import dataclass_transform
@dataclass_transform()
class ModelMeta(type):
pass
class Sensor(metaclass=ModelMeta):
key: int
@dataclass(frozen=True, kw_only=True)
class TemperatureSensor(Sensor):
name: str
t = TemperatureSensor(key=1, name="Temperature Sensor")
reveal_type(t.key) # revealed: int
reveal_type(t.name) # revealed: str
@@ -1096,18 +965,15 @@ enables use of `dataclasses.fields`, `dataclasses.asdict`, `dataclasses.replace`
from dataclasses import fields, asdict, replace, Field
from typing import dataclass_transform, Any
@dataclass_transform()
def create_model[T](cls: type[T]) -> type[T]:
return cls
@create_model
class Person:
name: str
age: int
p = Person("Alice", 30)
reveal_type(Person.__dataclass_fields__) # revealed: dict[str, Field[Any]]
@@ -1124,19 +990,15 @@ reveal_type(replace(p, name="Bob")) # revealed: Person
from dataclasses import fields, asdict, replace, Field
from typing import dataclass_transform, Any
@dataclass_transform()
class ModelMeta(type): ...
class ModelBase(metaclass=ModelMeta): ...
class Person(ModelBase):
name: str
age: int
p = Person("Alice", 30)
reveal_type(Person.__dataclass_fields__) # revealed: dict[str, Field[Any]]
@@ -1153,16 +1015,13 @@ reveal_type(replace(p, name="Bob")) # revealed: Person
from dataclasses import fields, asdict, replace, Field
from typing import dataclass_transform, Any
@dataclass_transform()
class ModelBase: ...
class Person(ModelBase):
name: str
age: int
p = Person("Alice", 30)
reveal_type(Person.__dataclass_fields__) # revealed: dict[str, Field[Any]]
@@ -1183,16 +1042,13 @@ When a function decorated with `@dataclass_transform()` is called directly with
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
def my_dataclass[T](cls: type[T]) -> type[T]:
return cls
class A:
x: int
B = my_dataclass(A)
reveal_type(B) # revealed: <class 'A'>
@@ -1205,16 +1061,13 @@ B(1)
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
def my_dataclass[T](cls: type[T], *, order: bool = False) -> type[T]:
return cls
class A:
x: int
B = my_dataclass(A, order=True)
reveal_type(B) # revealed: <class 'A'>
@@ -1230,7 +1083,6 @@ decorator), calling it with a class should return the class type.
```py
from typing_extensions import dataclass_transform, Callable, overload
@overload
@dataclass_transform()
def my_dataclass[T](cls: type[T]) -> type[T]: ...
@@ -1239,11 +1091,9 @@ def my_dataclass[T]() -> Callable[[type[T]], type[T]]: ...
def my_dataclass[T](cls: type[T] | None = None) -> type[T] | Callable[[type[T]], type[T]]:
raise NotImplementedError
class A:
x: int
B = my_dataclass(A)
reveal_type(B) # revealed: <class 'A'>
@@ -1259,16 +1109,13 @@ specialization should be preserved.
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
def my_dataclass[T](cls: type[T]) -> type[T]:
return cls
class A[T]:
x: T
B = my_dataclass(A[int])
reveal_type(B) # revealed: <class 'A[int]'>
@@ -1285,28 +1132,22 @@ class, not to the parameter class.
```py
from typing_extensions import dataclass_transform
@dataclass_transform()
def hydrated_dataclass[T](target: type[T], *, frozen: bool = False):
def decorator[U](cls: type[U]) -> type[U]:
return cls
return decorator
class Target:
pass
decorator = hydrated_dataclass(Target)
reveal_type(decorator) # revealed: <decorator produced by dataclass-like function>
@hydrated_dataclass(Target)
class Model:
x: int
# Model should be a dataclass-like class with x as a field
Model(x=1)
reveal_type(Model.__init__) # revealed: (self: Model, x: int) -> None

View File

@@ -5,14 +5,12 @@
```py
from dataclasses import dataclass, field
@dataclass
class Member:
name: str
role: str = field(default="user")
tag: str | None = field(default=None, init=False)
# revealed: (self: Member, name: str, role: str = "user") -> None
reveal_type(Member.__init__)
@@ -34,13 +32,11 @@ field:
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class Data:
content: list[int] = field(default_factory=list)
timestamp: datetime = field(default_factory=datetime.now, init=False)
# revealed: (self: Data, content: list[int] = ...) -> None
reveal_type(Data.__init__)
@@ -61,14 +57,12 @@ If `kw_only` is set to `True`, the field can only be set using keyword arguments
```py
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
age: int | None = field(default=None, kw_only=True)
role: str = field(default="user", kw_only=True)
# revealed: (self: Person, name: str, *, age: int | None = None, role: str = "user") -> None
reveal_type(Person.__init__)
@@ -83,11 +77,9 @@ bob = Person("Bob", 30)
```py
from dataclasses import field
def get_default() -> str:
return "default"
reveal_type(field(default=1)) # revealed: dataclasses.Field[Literal[1]]
reveal_type(field(default=None)) # revealed: dataclasses.Field[None]
reveal_type(field(default_factory=get_default)) # revealed: dataclasses.Field[str]
@@ -105,29 +97,23 @@ from typing import TypeVar
T = TypeVar("T")
@dataclass_transform()
def create_model(*, init: bool = True):
def deco(cls: type[T]) -> type[T]:
return cls
return deco
@create_model()
class A:
name: str = field(init=False)
# field(init=False) should be ignored for dataclass_transform without explicit field_specifiers
reveal_type(A.__init__) # revealed: (self: A, name: str) -> None
@dataclass
class B:
name: str = field(init=False)
# Regular @dataclass should respect field(init=False)
reveal_type(B.__init__) # revealed: (self: B) -> None
```

View File

@@ -13,11 +13,9 @@ of the decorator (which does not necessarily need to be a callable type):
def custom_decorator(f) -> int:
return 1
@custom_decorator
def f(x): ...
reveal_type(f) # revealed: int
```
@@ -28,16 +26,13 @@ More commonly, a decorator returns a modified callable type:
```py
from typing import Callable
def ensure_positive(wrapped: Callable[[int], bool]) -> Callable[[int], bool]:
return lambda x: wrapped(x) and x > 0
@ensure_positive
def even(x: int) -> bool:
return x % 2 == 0
reveal_type(even) # revealed: (int, /) -> bool
reveal_type(even(4)) # revealed: bool
```
@@ -50,19 +45,15 @@ arguments:
```py
from typing import Callable
def ensure_larger_than(lower_bound: int) -> Callable[[Callable[[int], bool]], Callable[[int], bool]]:
def decorator(wrapped: Callable[[int], bool]) -> Callable[[int], bool]:
return lambda x: wrapped(x) and x >= lower_bound
return decorator
@ensure_larger_than(10)
def even(x: int) -> bool:
return x % 2 == 0
reveal_type(even) # revealed: (int, /) -> bool
reveal_type(even(14)) # revealed: bool
```
@@ -76,21 +67,17 @@ meaning that the decorator closest to the function definition is applied first:
def maps_to_str(f) -> str:
return "a"
def maps_to_int(f) -> int:
return 1
def maps_to_bytes(f) -> bytes:
return b"a"
@maps_to_str
@maps_to_int
@maps_to_bytes
def f(x): ...
reveal_type(f) # revealed: str
```
@@ -110,12 +97,10 @@ class accept_strings:
def __call__(self, x: str | int) -> bool:
return self.f(int(x))
@accept_strings
def even(x: int) -> bool:
return x > 0
reveal_type(even) # revealed: accept_strings
reveal_type(even.custom_attribute) # revealed: str
reveal_type(even("1")) # revealed: bool
@@ -136,21 +121,17 @@ implemented using `functools.wraps`.
from typing import Callable
from functools import wraps
def custom_decorator(f) -> Callable[[int], str]:
@wraps(f)
def wrapper(*args, **kwargs):
print("Calling decorated function")
return f(*args, **kwargs)
return wrapper
@custom_decorator
def f(x: int) -> str:
return str(x)
reveal_type(f) # revealed: (int, /) -> str
```
@@ -159,12 +140,10 @@ reveal_type(f) # revealed: (int, /) -> str
```py
from functools import cache
@cache
def f(x: int) -> int:
return x**2
# revealed: _lru_cache_wrapper[int]
reveal_type(f)
# revealed: int
@@ -178,7 +157,6 @@ reveal_type(f(1))
def g(x: int) -> str:
return "a"
# TODO: This should be `Literal[g]` or `(int, /) -> str`
reveal_type(g) # revealed: Unknown
```
@@ -192,7 +170,6 @@ reveal_type(g) # revealed: Unknown
@unknown_decorator
def f(x): ...
reveal_type(f) # revealed: Unknown
```
@@ -203,7 +180,6 @@ reveal_type(f) # revealed: Unknown
@(1 + "a")
def f(x): ...
reveal_type(f) # revealed: Unknown
```
@@ -212,12 +188,10 @@ reveal_type(f) # revealed: Unknown
```py
non_callable = 1
# error: [call-non-callable] "Object of type `Literal[1]` is not callable"
@non_callable
def f(x): ...
reveal_type(f) # revealed: Unknown
```
@@ -232,12 +206,10 @@ first argument:
def wrong_signature(f: int) -> str:
return "a"
# error: [invalid-argument-type] "Argument to function `wrong_signature` is incorrect: Expected `int`, found `def f(x) -> Unknown`"
@wrong_signature
def f(x): ...
reveal_type(f) # revealed: str
```
@@ -249,19 +221,15 @@ Decorators need to be callable with a single argument. If they are not, we emit
def takes_two_arguments(f, g) -> str:
return "a"
# error: [missing-argument] "No argument provided for required parameter `g` of function `takes_two_arguments`"
@takes_two_arguments
def f(x): ...
reveal_type(f) # revealed: str
def takes_no_argument() -> str:
return "a"
# error: [too-many-positional-arguments] "Too many positional arguments to function `takes_no_argument`: expected 0, got 1"
@takes_no_argument
def g(x): ...
@@ -275,7 +243,6 @@ Class decorator calls are validated, emitting diagnostics for invalid arguments:
def takes_int(x: int) -> int:
return x
# error: [invalid-argument-type]
@takes_int
class Foo: ...
@@ -295,12 +262,10 @@ A decorator can enforce type constraints on the class being decorated:
def decorator(cls: type[int]) -> type[int]:
return cls
# error: [invalid-argument-type]
@decorator
class Baz: ...
# TODO: the revealed type should ideally be `type[int]` (the decorator's return type)
reveal_type(Baz) # revealed: <class 'Baz'>
```

View File

@@ -12,7 +12,6 @@ When a class defines `__eq__` and `__lt__`, the decorator synthesizes `__le__`,
```py
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, grade: int):
@@ -26,7 +25,6 @@ class Student:
def __lt__(self, other: "Student") -> bool:
return self.grade < other.grade
s1 = Student(85)
s2 = Student(90)
@@ -49,7 +47,6 @@ other than the class itself:
```py
from functools import total_ordering
@total_ordering
class Comparable:
def __init__(self, value: int):
@@ -69,7 +66,6 @@ class Comparable:
return self.value < other
return NotImplemented
a = Comparable(10)
b = Comparable(20)
@@ -92,7 +88,6 @@ overridden.
```py
from functools import total_ordering
@total_ordering
class MultiSig:
def __init__(self, value: int):
@@ -100,16 +95,13 @@ class MultiSig:
def __eq__(self, other: object) -> bool:
return True
# __lt__ accepts `object` (highest priority, used as root)
def __lt__(self, other: object) -> bool:
return True
# __gt__ only accepts `MultiSig` (not overridden by decorator)
def __gt__(self, other: "MultiSig") -> bool:
return True
a = MultiSig(10)
b = MultiSig(20)
@@ -133,7 +125,6 @@ all overloads:
from functools import total_ordering
from typing import overload
@total_ordering
class Flexible:
def __init__(self, value: int):
@@ -151,7 +142,6 @@ class Flexible:
return self.value < other.value
return self.value < other
a = Flexible(10)
b = Flexible(20)
@@ -175,7 +165,6 @@ When a class defines `__eq__` and `__gt__`, the decorator synthesizes `__lt__`,
```py
from functools import total_ordering
@total_ordering
class Priority:
def __init__(self, level: int):
@@ -189,7 +178,6 @@ class Priority:
def __gt__(self, other: "Priority") -> bool:
return self.level > other.level
p1 = Priority(1)
p2 = Priority(2)
@@ -211,7 +199,6 @@ A class only needs to define a single comparison method. The `__eq__` method can
```py
from functools import total_ordering
@total_ordering
class Score:
def __init__(self, value: int):
@@ -220,7 +207,6 @@ class Score:
def __lt__(self, other: "Score") -> bool:
return self.value < other.value
s1 = Score(85)
s2 = Score(90)
@@ -240,12 +226,10 @@ The decorator also works when the ordering method is inherited from a superclass
```py
from functools import total_ordering
class Base:
def __lt__(self, other: "Base") -> bool:
return True
@total_ordering
class Child(Base):
def __eq__(self, other: object) -> bool:
@@ -253,7 +237,6 @@ class Child(Base):
return NotImplemented
return True
c1 = Child()
c2 = Child()
@@ -273,19 +256,16 @@ over the locally-defined `__gt__`:
from functools import total_ordering
from typing import Literal
class Base:
def __lt__(self, other: "Base") -> Literal[True]:
return True
@total_ordering
class Child(Base):
# __gt__ is defined locally, but __lt__ (inherited) takes precedence
def __gt__(self, other: "Child") -> Literal[False]:
return False
c1 = Child()
c2 = Child()
@@ -310,7 +290,6 @@ We use a narrower return type (`Literal[True]`) to verify that the explicit meth
from functools import total_ordering
from typing import Literal
@total_ordering
class Temperature:
def __init__(self, celsius: float):
@@ -322,7 +301,6 @@ class Temperature:
def __gt__(self, other: "Temperature") -> Literal[True]:
return True
t1 = Temperature(20.0)
t2 = Temperature(25.0)
@@ -343,7 +321,6 @@ The decorator works with `@dataclass`:
from dataclasses import dataclass
from functools import total_ordering
@total_ordering
@dataclass
class Point:
@@ -353,7 +330,6 @@ class Point:
def __lt__(self, other: "Point") -> bool:
return (self.x, self.y) < (other.x, other.y)
p1 = Point(1, 2)
p2 = Point(3, 4)
@@ -377,13 +353,11 @@ a diagnostic is emitted at the decorator site:
```py
from functools import total_ordering
@total_ordering # error: [invalid-total-ordering]
class NoOrdering:
def __eq__(self, other: object) -> bool:
return True
n1 = NoOrdering()
n2 = NoOrdering()
@@ -410,7 +384,6 @@ class NoDecorator:
def __lt__(self, other: "NoDecorator") -> bool:
return self.value < other.value
n1 = NoDecorator(1)
n2 = NoDecorator(2)
@@ -443,7 +416,6 @@ simplifies to `int`:
```py
from functools import total_ordering
@total_ordering
class IntReturn:
def __init__(self, value: int):
@@ -457,7 +429,6 @@ class IntReturn:
def __lt__(self, other: "IntReturn") -> int:
return self.value - other.value
a = IntReturn(10)
b = IntReturn(20)
@@ -476,7 +447,6 @@ When the root method returns a type that is not a supertype of `bool`, the union
```py
from functools import total_ordering
@total_ordering
class StrReturn:
def __init__(self, value: str):
@@ -490,7 +460,6 @@ class StrReturn:
def __lt__(self, other: "StrReturn") -> str:
return self.value
a = StrReturn("a")
b = StrReturn("b")
@@ -511,12 +480,10 @@ performed:
```py
from functools import total_ordering
class NoOrderingMethod:
def __eq__(self, other: object) -> bool:
return True
# error: [invalid-total-ordering]
InvalidOrderedClass = total_ordering(NoOrderingMethod)
```
@@ -526,7 +493,6 @@ When the class does define an ordering method, no error is emitted:
```py
from functools import total_ordering
class HasOrderingMethod:
def __eq__(self, other: object) -> bool:
return True
@@ -534,108 +500,7 @@ class HasOrderingMethod:
def __lt__(self, other: "HasOrderingMethod") -> bool:
return True
# No error (class defines `__lt__`).
ValidOrderedClass = total_ordering(HasOrderingMethod)
reveal_type(ValidOrderedClass) # revealed: type[HasOrderingMethod]
```
## Function call form with `type()`
When `total_ordering` is called on a class created with `type()`, the same validation is performed:
```py
from functools import total_ordering
def lt_impl(self, other) -> bool:
return True
# No error: the functional class defines `__lt__` in its namespace
ValidFunctional = total_ordering(type("ValidFunctional", (), {"__lt__": lt_impl}))
InvalidFunctionalBase = type("InvalidFunctionalBase", (), {})
# error: [invalid-total-ordering]
InvalidFunctional = total_ordering(InvalidFunctionalBase)
```
## Inherited from functional class
When a class inherits from a functional class that defines an ordering method, `@total_ordering`
correctly detects it:
```py
from functools import total_ordering
def lt_impl(self, other) -> bool:
return True
def eq_impl(self, other) -> bool:
return True
# Functional class with __lt__ method
OrderedBase = type("OrderedBase", (), {"__lt__": lt_impl})
# A class inheriting from OrderedBase gets the ordering method
@total_ordering
class Ordered(OrderedBase):
def __eq__(self, other: object) -> bool:
return True
o1 = Ordered()
o2 = Ordered()
# Inherited __lt__ is available
reveal_type(o1 < o2) # revealed: bool
# @total_ordering synthesizes the other methods
reveal_type(o1 <= o2) # revealed: bool
reveal_type(o1 > o2) # revealed: bool
reveal_type(o1 >= o2) # revealed: bool
```
When the dynamic base class does not define any ordering method, `@total_ordering` emits an error:
```py
from functools import total_ordering
# Dynamic class without ordering methods (invalid for @total_ordering)
NoOrderBase = type("NoOrderBase", (), {})
@total_ordering # error: [invalid-total-ordering]
class NoOrder(NoOrderBase):
def __eq__(self, other: object) -> bool:
return True
```
## Dynamic namespace
When a `type()`-constructed class has a dynamic namespace, we assume it might provide an ordering
method (since we can't know what's in the namespace). No error is emitted when such a class is
passed to `@total_ordering`:
```py
from functools import total_ordering
from typing import Any
def f(ns: dict[str, Any]):
# Dynamic class with dynamic namespace - might have ordering methods
DynamicBase = type("DynamicBase", (), ns)
# No error: the dynamic namespace might contain __lt__ or another ordering method
@total_ordering
class Ordered(DynamicBase):
def __eq__(self, other: object) -> bool:
return True
# Also works when calling total_ordering as a function
OrderedDirect = total_ordering(type("OrderedDirect", (), ns))
```

View File

@@ -21,11 +21,9 @@ reveal_type(x) # revealed: Unknown
# error: [unresolved-reference]
reveal_type(y) # revealed: Unknown
def cond() -> bool:
return True
b = 1
if cond():
del b
@@ -44,21 +42,17 @@ reveal_type(c) # revealed: Literal[2]
d = [1, 2, 3]
def delete():
del d # error: [unresolved-reference] "Name `d` used when not defined"
delete()
reveal_type(d) # revealed: list[Unknown | int]
def delete_element():
# When the `del` target isn't a name, it doesn't force local resolution.
del d[0]
print(d)
def delete_global():
global d
del d
@@ -66,12 +60,10 @@ def delete_global():
# be careful about false positives if `d` got reinitialized somehow in between the two `del`s.
del d
delete_global()
# Again, the variable should have been removed, but we don't check it.
reveal_type(d) # revealed: list[Unknown | int]
def delete_nonlocal():
e = 2
@@ -94,7 +86,6 @@ local error:
```py
x = 1
def foo():
print(x) # error: [unresolved-reference] "Name `x` used when not defined"
if False:
@@ -113,14 +104,11 @@ However, with `global x` in `foo`, `print(x)` in `bar` resolves in the global sc
```py
x = 1
def foo():
global x
def bar():
# allowed, refers to `x` in the global scope
reveal_type(x) # revealed: Literal[1]
bar()
del x # allowed, deletes `x` in the global scope (though we don't track that)
```
@@ -131,14 +119,11 @@ refer to:
```py
def enclosing():
x = 2
def foo():
nonlocal x
def bar():
# allowed, refers to `x` in `enclosing`
reveal_type(x) # revealed: Literal[2]
bar()
del x # allowed, deletes `x` in `enclosing` (though we don't track that)
```
@@ -156,7 +141,6 @@ assignment, and the attribute type will be the originally declared type.
class C:
x: int = 1
c = C()
del c.x
reveal_type(c.x) # revealed: int
@@ -176,7 +160,6 @@ reveal_type(c.x) # revealed: int
class C:
x: int = 1
c = C()
reveal_type(c.x) # revealed: int
@@ -219,21 +202,17 @@ from typing import Protocol, TypeVar
KT = TypeVar("KT")
class CanDelItem(Protocol[KT]):
def __delitem__(self, k: KT, /) -> None: ...
def f(x: CanDelItem[int], k: int):
# This should be valid - the object has __delitem__
del x[k]
class OnlyDelItem:
def __delitem__(self, key: int) -> None:
pass
d = OnlyDelItem()
del d[0] # OK
@@ -250,7 +229,6 @@ class OnlyGetItem:
def __getitem__(self, key: int) -> str:
return "value"
g = OnlyGetItem()
reveal_type(g[0]) # revealed: str
@@ -269,22 +247,18 @@ a valid instance of that TypedDict type. However, deleting `NotRequired` keys (o
```py
from typing_extensions import TypedDict, NotRequired
class Movie(TypedDict):
name: str
year: int
class PartialMovie(TypedDict, total=False):
name: str
year: int
class MixedMovie(TypedDict):
name: str
year: NotRequired[int]
m: Movie = {"name": "Blade Runner", "year": 1982}
p: PartialMovie = {"name": "Test"}
mixed: MixedMovie = {"name": "Test"}

View File

@@ -10,36 +10,30 @@ classes. Uses of these items should subsequently produce a warning.
```py
from typing_extensions import deprecated
@deprecated("use OtherClass")
def myfunc(x: int): ...
myfunc(1) # error: [deprecated] "use OtherClass"
```
```py
from typing_extensions import deprecated
@deprecated("use BetterClass")
class MyClass: ...
MyClass() # error: [deprecated] "use BetterClass"
```
```py
from typing_extensions import deprecated
class MyClass:
@deprecated("use something else")
def afunc(): ...
@deprecated("don't use this!")
def amethod(self): ...
MyClass.afunc() # error: [deprecated] "use something else"
MyClass().amethod() # error: [deprecated] "don't use this!"
```
@@ -65,22 +59,18 @@ runtime behavior.
```py
from typing_extensions import deprecated
@deprecated # error: [invalid-argument-type] "LiteralString"
def invalid_deco(): ...
invalid_deco() # error: [missing-argument]
```
```py
from typing_extensions import deprecated
@deprecated() # error: [missing-argument] "message"
def invalid_deco(): ...
invalid_deco()
```
@@ -92,11 +82,9 @@ from typing_extensions import deprecated
x = "message"
@deprecated(x)
def invalid_deco(): ...
invalid_deco() # error: [deprecated] "message"
```
@@ -105,15 +93,12 @@ However sufficiently opaque LiteralStrings we can't resolve, and so we lose the
```py
from typing_extensions import deprecated, LiteralString
def opaque() -> LiteralString:
return "message"
@deprecated(opaque())
def valid_deco(): ...
valid_deco() # error: [deprecated]
```
@@ -123,15 +108,12 @@ LiteralString, so we can/should emit a diagnostic for this:
```py
from typing_extensions import deprecated
def opaque() -> str:
return "message"
@deprecated(opaque()) # error: [invalid-argument-type] "LiteralString"
def dubious_deco(): ...
dubious_deco()
```
@@ -140,11 +122,9 @@ Although we have no use for the other arguments, we should still error if they'r
```py
from typing_extensions import deprecated
@deprecated("some message", dsfsdf="whatever") # error: [unknown-argument] "dsfsdf"
def invalid_deco(): ...
invalid_deco()
```
@@ -153,11 +133,9 @@ And we should always handle correct ones fine.
```py
from typing_extensions import deprecated
@deprecated("some message", category=DeprecationWarning, stacklevel=1)
def valid_deco(): ...
valid_deco() # error: [deprecated] "some message"
```
@@ -177,13 +155,11 @@ python-version = "3.13"
import warnings
import typing_extensions
@warnings.deprecated("nope")
def func1(): ...
@typing_extensions.deprecated("nada")
def func2(): ...
func1() # error: [deprecated] "nope"
func2() # error: [deprecated] "nada"
```
@@ -200,11 +176,9 @@ shouldn't produce a warning.
```py
from typing_extensions import deprecated
@deprecated("Use OtherType instead")
class DeprType: ...
@deprecated("Use other_func instead")
def depr_func(): ...
```
@@ -220,10 +194,8 @@ from module import DeprType, depr_func
DeprType() # error: [deprecated] "Use OtherType instead"
depr_func() # error: [deprecated] "Use other_func instead"
def higher_order(x): ...
# TODO: these diagnostics ideally shouldn't fire since we warn on the import
higher_order(DeprType) # error: [deprecated] "Use OtherType instead"
higher_order(depr_func) # error: [deprecated] "Use other_func instead"
@@ -243,11 +215,9 @@ a warning.
```py
from typing_extensions import deprecated
@deprecated("Use OtherType instead")
class DeprType: ...
@deprecated("Use other_func instead")
def depr_func(): ...
```
@@ -260,10 +230,8 @@ import module
module.DeprType() # error: [deprecated] "Use OtherType instead"
module.depr_func() # error: [deprecated] "Use other_func instead"
def higher_order(x): ...
higher_order(module.DeprType) # error: [deprecated] "Use OtherType instead"
higher_order(module.depr_func) # error: [deprecated] "Use other_func instead"
@@ -280,11 +248,9 @@ If the items are instead star-imported, then the actual uses should warn.
```py
from typing_extensions import deprecated
@deprecated("Use OtherType instead")
class DeprType: ...
@deprecated("Use other_func instead")
def depr_func(): ...
```
@@ -297,10 +263,8 @@ from module import *
DeprType() # error: [deprecated] "Use OtherType instead"
depr_func() # error: [deprecated] "Use other_func instead"
def higher_order(x): ...
higher_order(DeprType) # error: [deprecated] "Use OtherType instead"
higher_order(depr_func) # error: [deprecated] "Use other_func instead"
@@ -317,15 +281,12 @@ redundant and annoying.
```py
from typing_extensions import deprecated
@deprecated("Use OtherType instead")
class DeprType: ...
@deprecated("Use other_func instead")
def depr_func(): ...
alias_func = depr_func # error: [deprecated] "Use other_func instead"
AliasClass = DeprType # error: [deprecated] "Use OtherType instead"
@@ -342,7 +303,6 @@ diagnostic.
```py
from typing_extensions import deprecated
class MyInt:
def __init__(self, val):
self.val = val
@@ -351,7 +311,6 @@ class MyInt:
def __add__(self, other):
return MyInt(self.val + other.val)
x = MyInt(1)
y = MyInt(2)
z = x + y # TODO error: [deprecated] "MyInt `+` support is broken"
@@ -365,7 +324,6 @@ Overloads can be deprecated, but only trigger warnings when invoked.
from typing_extensions import deprecated
from typing_extensions import overload
@overload
@deprecated("strings are no longer supported")
def f(x: str): ...
@@ -374,7 +332,6 @@ def f(x: int): ...
def f(x):
print(x)
f(1)
f("hello") # TODO: error: [deprecated] "strings are no longer supported"
```
@@ -385,7 +342,6 @@ If the actual impl is deprecated, the deprecation always fires.
from typing_extensions import deprecated
from typing_extensions import overload
@overload
def f(x: str): ...
@overload
@@ -394,7 +350,6 @@ def f(x: int): ...
def f(x):
print(x)
f(1) # error: [deprecated] "unusable"
f("hello") # error: [deprecated] "unusable"
```

View File

@@ -16,7 +16,6 @@ descriptor that returns a constant value:
```py
from typing import Literal
class Ten:
def __get__(self, instance: object, owner: type | None = None) -> Literal[10]:
return 10
@@ -24,11 +23,9 @@ class Ten:
def __set__(self, instance: object, value: Literal[10]) -> None:
pass
class C:
ten: Ten = Ten()
c = C()
reveal_type(c.ten) # revealed: Literal[10]
@@ -69,11 +66,9 @@ class FlexibleInt:
def __set__(self, instance: object, value: int | str) -> None:
self._value = int(value)
class C:
flexible_int: FlexibleInt = FlexibleInt()
c = C()
reveal_type(c.flexible_int) # revealed: int | None
@@ -102,7 +97,6 @@ non-data descriptors.
```py
from typing import Literal
class DataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> Literal["data"]:
return "data"
@@ -110,12 +104,10 @@ class DataDescriptor:
def __set__(self, instance: object, value: int) -> None:
pass
class NonDataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> Literal["non-data"]:
return "non-data"
class C:
data_descriptor = DataDescriptor()
non_data_descriptor = NonDataDescriptor()
@@ -131,7 +123,6 @@ class C:
# So it is possible to override them.
self.non_data_descriptor = 1
c = C()
reveal_type(c.data_descriptor) # revealed: Unknown | Literal["data"]
@@ -157,7 +148,6 @@ all possible results accordingly. We start by defining a data and a non-data des
```py
from typing import Literal
class DataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> Literal["data"]:
return "data"
@@ -165,7 +155,6 @@ class DataDescriptor:
def __set__(self, instance: object, value: int) -> None:
pass
class NonDataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> Literal["non-data"]:
return "non-data"
@@ -197,10 +186,8 @@ descriptor here:
class C2:
def f(self):
self.attr = "normal"
attr = NonDataDescriptor()
reveal_type(C2().attr) # revealed: Unknown | Literal["non-data", "normal"]
# Assignments always go to the instance attribute in this case
@@ -214,17 +201,14 @@ Descriptors only work when used as class variables. When put in instances, they
```py
from typing import Literal
class Ten:
def __get__(self, instance: object, owner: type | None = None) -> Literal[10]:
return 10
class C:
def __init__(self):
self.ten: Ten = Ten()
reveal_type(C().ten) # revealed: Ten
C().ten = Ten()
@@ -249,7 +233,6 @@ To verify this, we define a data and a non-data descriptor:
```py
from typing import Literal, Any
class DataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> Literal["data"]:
return "data"
@@ -257,7 +240,6 @@ class DataDescriptor:
def __set__(self, instance: object, value: int) -> None:
pass
class NonDataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> Literal["non-data"]:
return "non-data"
@@ -271,12 +253,10 @@ class Meta1(type):
meta_data_descriptor: DataDescriptor = DataDescriptor()
meta_non_data_descriptor: NonDataDescriptor = NonDataDescriptor()
class C1(metaclass=Meta1):
class_data_descriptor: DataDescriptor = DataDescriptor()
class_non_data_descriptor: NonDataDescriptor = NonDataDescriptor()
reveal_type(C1.meta_data_descriptor) # revealed: Literal["data"]
reveal_type(C1.meta_non_data_descriptor) # revealed: Literal["non-data"]
@@ -313,7 +293,6 @@ class Meta2(type):
meta_data_descriptor1: DataDescriptor = DataDescriptor()
meta_data_descriptor2: DataDescriptor = DataDescriptor()
class ClassLevelDataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> Literal["class level data descriptor"]:
return "class level data descriptor"
@@ -321,12 +300,10 @@ class ClassLevelDataDescriptor:
def __set__(self, instance: object, value: str) -> None:
pass
class C2(metaclass=Meta2):
meta_data_descriptor1: Literal["value on class"] = "value on class"
meta_data_descriptor2: ClassLevelDataDescriptor = ClassLevelDataDescriptor()
reveal_type(C2.meta_data_descriptor1) # revealed: Literal["data"]
reveal_type(C2.meta_data_descriptor2) # revealed: Literal["data"]
@@ -349,14 +326,12 @@ class Meta3(type):
meta_non_data_descriptor1: NonDataDescriptor = NonDataDescriptor()
meta_non_data_descriptor2: NonDataDescriptor = NonDataDescriptor()
class C3(metaclass=Meta3):
meta_attribute1: Literal["value on class"] = "value on class"
meta_attribute2: ClassLevelDataDescriptor = ClassLevelDataDescriptor()
meta_non_data_descriptor1: Literal["value on class"] = "value on class"
meta_non_data_descriptor2: ClassLevelDataDescriptor = ClassLevelDataDescriptor()
reveal_type(C3.meta_attribute1) # revealed: Literal["value on class"]
reveal_type(C3.meta_attribute2) # revealed: Literal["class level data descriptor"]
reveal_type(C3.meta_non_data_descriptor1) # revealed: Literal["value on class"]
@@ -371,10 +346,8 @@ class Meta4(type):
meta_attribute: Literal["value on metaclass"] = "value on metaclass"
meta_non_data_descriptor: NonDataDescriptor = NonDataDescriptor()
class C4(metaclass=Meta4): ...
reveal_type(C4.meta_attribute) # revealed: Literal["value on metaclass"]
reveal_type(C4.meta_non_data_descriptor) # revealed: Literal["non-data"]
```
@@ -413,7 +386,6 @@ metaclass attribute (unless it's a data descriptor, which always takes precedenc
```py
from typing import Any
def _(flag: bool):
class Meta6(type):
attribute1: DataDescriptor = DataDescriptor()
@@ -476,7 +448,6 @@ when it is accessed on an instance. A real-world example of this is the `__get__
```py
from typing_extensions import Literal, LiteralString, overload
class Descriptor:
@overload
def __get__(self, instance: None, owner: type, /) -> Literal["called on class object"]: ...
@@ -488,11 +459,9 @@ class Descriptor:
else:
return "called on class object"
class C:
d: Descriptor = Descriptor()
reveal_type(C.d) # revealed: Literal["called on class object"]
reveal_type(C().d) # revealed: Literal["called on instance"]
@@ -510,16 +479,13 @@ class SomeCallable:
def __call__(self, x: int) -> str:
return "a"
class Descriptor:
def __get__(self, instance: object, owner: type | None = None) -> SomeCallable:
return SomeCallable()
class B:
__call__: Descriptor = Descriptor()
b_instance = B()
reveal_type(b_instance(1)) # revealed: str
@@ -546,7 +512,6 @@ class C:
def name(self, value: str | None) -> None:
self._value = value
c = C()
reveal_type(c._name) # revealed: str | None
@@ -585,7 +550,6 @@ class Base:
def other(self, v: float) -> None:
self.value = v
class Derived(Base):
@property
def other(self) -> float:
@@ -609,7 +573,6 @@ class DontAssignToMe:
@property
def immutable(self): ...
# error: [invalid-assignment]
DontAssignToMe().immutable = "the properties, they are a-changing"
```
@@ -632,7 +595,6 @@ class C:
def get_name(cls) -> str:
return cls.__name__
c1 = C.factory("test") # okay
reveal_type(c1) # revealed: C
@@ -650,7 +612,6 @@ class C:
def helper(value: str) -> str:
return value
reveal_type(C.helper("42")) # revealed: str
c = C()
reveal_type(c.helper("string")) # revealed: str
@@ -667,11 +628,9 @@ import types
from inspect import getattr_static
from ty_extensions import static_assert, is_subtype_of, TypeOf
def f(x: object) -> str:
return "a"
reveal_type(f) # revealed: def f(x: object) -> str
reveal_type(f.__get__) # revealed: <method-wrapper '__get__' of function 'f'>
static_assert(is_subtype_of(TypeOf[f.__get__], types.MethodWrapperType))
@@ -696,7 +655,6 @@ We can also bind the free function `f` to an instance of a class `C`:
```py
class C: ...
bound_method = wrapper_descriptor(f, C(), C)
reveal_type(bound_method) # revealed: bound method C.f() -> str
@@ -750,31 +708,25 @@ This test makes sure that we call `__get__` with the right argument types for va
```py
from __future__ import annotations
class TailoredForClassObjectAccess:
def __get__(self, instance: None, owner: type[C]) -> int:
return 1
class TailoredForInstanceAccess:
def __get__(self, instance: C, owner: type[C] | None = None) -> str:
return "a"
class TailoredForMetaclassAccess:
def __get__(self, instance: type[C], owner: type[Meta]) -> bytes:
return b"a"
class Meta(type):
metaclass_access: TailoredForMetaclassAccess = TailoredForMetaclassAccess()
class C(metaclass=Meta):
class_object_access: TailoredForClassObjectAccess = TailoredForClassObjectAccess()
instance_access: TailoredForInstanceAccess = TailoredForInstanceAccess()
reveal_type(C.class_object_access) # revealed: int
reveal_type(C().instance_access) # revealed: str
reveal_type(C.metaclass_access) # revealed: bytes
@@ -800,11 +752,9 @@ class Descriptor:
def __get__(self) -> int:
return 1
class C:
descriptor: Descriptor = Descriptor()
# TODO: This should be an error
reveal_type(C.descriptor) # revealed: int
@@ -822,11 +772,9 @@ call `__get__`" on the descriptor object (leading us to infer `Unknown`):
class BrokenDescriptor:
__get__: None = None
class Foo:
desc: BrokenDescriptor = BrokenDescriptor()
# TODO: this raises `TypeError` at runtime due to the implicit call to `__get__`;
# we should emit a diagnostic
reveal_type(Foo().desc) # revealed: Unknown
@@ -846,11 +794,9 @@ class Descriptor:
def __set__(self, instance: object, value: int) -> None:
pass
class C:
descriptor = Descriptor()
C.descriptor = "something else"
reveal_type(C.descriptor) # revealed: Literal["something else"]
```
@@ -865,12 +811,10 @@ class DataDescriptor:
def __set__(self, instance: int, value) -> None:
pass
class NonDataDescriptor:
def __get__(self, instance: object, owner: type | None = None) -> int:
return 1
def _(flag: bool):
class PossiblyUnbound:
if flag:
@@ -896,7 +840,6 @@ def _(flag: bool):
def _(flag: bool):
class MaybeDescriptor:
if flag:
def __get__(self, instance: object, owner: type | None = None) -> int:
return 1
@@ -916,46 +859,36 @@ descriptor protocol on the callable's `__call__` method:
```py
from __future__ import annotations
class ReturnedCallable2:
def __call__(self, descriptor: Descriptor1, instance: None, owner: type[C]) -> int:
return 1
class ReturnedCallable1:
def __call__(self, descriptor: Descriptor2, instance: Callable1, owner: type[Callable1]) -> ReturnedCallable2:
return ReturnedCallable2()
class Callable3:
def __call__(self, descriptor: Descriptor3, instance: Callable2, owner: type[Callable2]) -> ReturnedCallable1:
return ReturnedCallable1()
class Descriptor3:
__get__: Callable3 = Callable3()
class Callable2:
__call__: Descriptor3 = Descriptor3()
class Descriptor2:
__get__: Callable2 = Callable2()
class Callable1:
__call__: Descriptor2 = Descriptor2()
class Descriptor1:
__get__: Callable1 = Callable1()
class C:
d: Descriptor1 = Descriptor1()
reveal_type(C.d) # revealed: int
```

View File

@@ -13,7 +13,6 @@ These can be set on instances and on class objects.
class C:
attr: int = 0
instance = C()
instance.attr = 1 # fine
instance.attr = "wrong" # error: [invalid-assignment]
@@ -32,7 +31,6 @@ class C:
def __init__(self):
self.attr: int = 0
instance = C()
instance.attr = 1 # fine
instance.attr = "wrong" # error: [invalid-assignment]
@@ -48,11 +46,9 @@ diagnostic that mentions that the attribute is only available on class objects.
```py
from typing import ClassVar
class C:
attr: ClassVar[int] = 0
C.attr = 1 # fine
C.attr = "wrong" # error: [invalid-assignment]
@@ -67,7 +63,6 @@ When trying to set an attribute that is not defined, we also emit errors:
```py
class C: ...
C.non_existent = 1 # error: [unresolved-attribute]
instance = C()
@@ -102,11 +97,9 @@ class Descriptor:
def __set__(self, instance: object, value: int) -> None:
pass
class C:
attr: Descriptor = Descriptor()
instance = C()
instance.attr = 1 # fine
@@ -121,11 +114,9 @@ class WrongDescriptor:
def __set__(self, instance: object, value: int, extra: int) -> None:
pass
class C:
attr: WrongDescriptor = WrongDescriptor()
instance = C()
# TODO: ideally, we would mention why this is an invalid assignment (wrong number of arguments for `__set__`)
@@ -137,12 +128,10 @@ instance.attr = 1 # error: [invalid-assignment]
```py
def _(flag: bool) -> None:
if flag:
class C1:
attr: int = 0
else:
class C1:
attr: str = ""

View File

@@ -11,7 +11,6 @@ to the invalid argument.
def foo(x: int) -> int:
return x * x
foo("hello") # error: [invalid-argument-type]
```
@@ -23,7 +22,6 @@ This is like the basic test, except we put the call site above the function defi
def bar():
foo("hello") # error: [invalid-argument-type]
def foo(x: int) -> int:
return x * x
```
@@ -54,7 +52,6 @@ This checks that a diagnostic renders reasonably when there are multiple paramet
def foo(x: int, y: int, z: int) -> int:
return x * y * z
foo(1, "hello", 3) # error: [invalid-argument-type]
```
@@ -71,7 +68,6 @@ def foo(
) -> int:
return x * y * z
foo(1, "hello", 3) # error: [invalid-argument-type]
```
@@ -84,7 +80,6 @@ invalid argument types.
def foo(x: int, y: int, z: int) -> int:
return x * y * z
# error: [invalid-argument-type]
# error: [invalid-argument-type]
# error: [invalid-argument-type]
@@ -119,7 +114,6 @@ Tests a function definition with only positional parameters.
def foo(x: int, y: int, z: int, /) -> int:
return x * y * z
foo(1, "hello", 3) # error: [invalid-argument-type]
```
@@ -131,7 +125,6 @@ Tests a function definition with variadic arguments.
def foo(*numbers: int) -> int:
return len(numbers)
foo(1, 2, 3, "hello", 5) # error: [invalid-argument-type]
```
@@ -143,7 +136,6 @@ Tests a function definition with keyword-only arguments.
def foo(x: int, y: int, *, z: int = 0) -> int:
return x * y * z
foo(1, 2, z="hello") # error: [invalid-argument-type]
```
@@ -155,7 +147,6 @@ Tests a function definition with keyword-only arguments.
def foo(x: int, y: int, z: int = 0) -> int:
return x * y * z
foo(1, 2, "hello") # error: [invalid-argument-type]
```
@@ -165,7 +156,6 @@ foo(1, 2, "hello") # error: [invalid-argument-type]
def foo(**numbers: int) -> int:
return len(numbers)
foo(a=1, b=2, c=3, d="hello", e=5) # error: [invalid-argument-type]
```
@@ -177,7 +167,6 @@ Tests a function definition with multiple different kinds of arguments.
def foo(x: int, /, y: int, *, z: int = 0) -> int:
return x * y * z
foo(1, 2, z="hello") # error: [invalid-argument-type]
```
@@ -190,7 +179,6 @@ class C:
def __call__(self, x: int) -> int:
return 1
c = C()
c("wrong") # error: [invalid-argument-type]
```
@@ -204,7 +192,6 @@ class C:
def square(self, x: int) -> int:
return x * x
c = C()
c.square("hello") # error: [invalid-argument-type]
```
@@ -216,7 +203,6 @@ c.square("hello") # error: [invalid-argument-type]
```py
class Foo: ...
def needs_a_foo(x: Foo): ...
```
@@ -225,10 +211,8 @@ def needs_a_foo(x: Foo): ...
```py
from module import needs_a_foo
class Foo: ...
needs_a_foo(Foo()) # error: [invalid-argument-type]
```
@@ -246,7 +230,6 @@ python-version = "3.12"
```py
class Foo: ...
def needs_a_foo(x: Foo): ...
```
@@ -255,10 +238,8 @@ def needs_a_foo(x: Foo): ...
```py
from module import needs_a_foo
class Foo: ...
def f[T: Foo](x: T) -> T:
needs_a_foo(x) # error: [invalid-argument-type]
return x

View File

@@ -19,7 +19,6 @@ This diagnostic also points to the class definition if available.
class MissingAwait:
pass
async def main() -> None:
await MissingAwait() # error: [invalid-await]
```
@@ -31,14 +30,11 @@ This diagnostic also points to the method definition if available.
```py
from datetime import datetime
class PossiblyUnbound:
if datetime.today().weekday() == 0:
def __await__(self):
yield
async def main() -> None:
await PossiblyUnbound() # error: [invalid-await]
```
@@ -54,7 +50,6 @@ class InvalidAwaitArgs:
def __await__(self, value: int):
yield value
async def main() -> None:
await InvalidAwaitArgs() # error: [invalid-await]
```
@@ -68,7 +63,6 @@ awaitable.
class NonCallableAwait:
__await__ = 42
async def main() -> None:
await NonCallableAwait() # error: [invalid-await]
```
@@ -83,7 +77,6 @@ class InvalidAwaitReturn:
def __await__(self) -> int:
return 5
async def main() -> None:
await InvalidAwaitReturn() # error: [invalid-await]
```
@@ -97,19 +90,16 @@ instance to be awaitable. In this specific case, no specific function definition
import typing
from datetime import datetime
class UnawaitableUnion:
if datetime.today().weekday() == 6:
def __await__(self) -> typing.Generator[typing.Any, None, None]:
yield
else:
def __await__(self) -> int:
return 5
async def main() -> None:
await UnawaitableUnion() # error: [invalid-await]
```

View File

@@ -17,32 +17,25 @@ T3 = TypeVar("T3")
DefaultStrT = TypeVar("DefaultStrT", default=str)
class SubclassMe(Generic[T1, DefaultStrT]):
x: DefaultStrT
class Baz(SubclassMe[int, DefaultStrT]):
pass
# error: [invalid-generic-class] "Type parameter `T2` without a default cannot follow earlier parameter `T1` with a default"
class Foo(Generic[T1, T2]):
pass
class Bar(Generic[T2, T1, T3]): # error: [invalid-generic-class]
pass
class Spam(Generic[T1, T2, DefaultStrT, T3]): # error: [invalid-generic-class]
pass
class Ham(Protocol[T1, T2, DefaultStrT, T3]): # error: [invalid-generic-class]
pass
class VeryBad(
Protocol[T1, T2, DefaultStrT, T3], # error: [invalid-generic-class]
Generic[T1, T2, DefaultStrT, T3],

View File

@@ -102,11 +102,9 @@ T = TypeVar("T", covariant=True, contravariant=True)
```py
from typing_extensions import TypeVar
def cond() -> bool:
return True
# error: [invalid-legacy-type-variable]
T = TypeVar("T", covariant=cond())

View File

@@ -13,7 +13,6 @@ too verbose for it to be worth it.
def f(a, b=42): ...
def g(a, b): ...
class Foo:
def method(self, a): ...
```
@@ -25,11 +24,9 @@ from module import f, g, Foo
f() # error: [missing-argument]
def coinflip() -> bool:
return True
h = f if coinflip() else g
# error: [missing-argument]

View File

@@ -7,7 +7,6 @@
```py
from typing import overload
@overload
def f(x: int) -> int: ...
@overload
@@ -15,7 +14,6 @@ def f(x: str) -> str: ...
def f(x: int | str) -> int | str:
return x
f(b"foo") # error: [no-matching-overload]
```
@@ -29,10 +27,8 @@ Which in turn makes snapshotting a bit annoying, since the output can depend on
```py
from typing import overload
class Foo: ...
@overload
def foo(a: int, b: int, c: int): ...
@overload
@@ -77,7 +73,6 @@ def foo(a: str, b: float, c: float): ...
def foo(a: float, b: float, c: float): ...
def foo(a, b, c): ...
foo(Foo(), Foo()) # error: [no-matching-overload]
```
@@ -89,10 +84,8 @@ cut off the list in the diagnostic and emit a message stating the number of omit
```py
from typing import overload
class Foo: ...
@overload
def foo(a: int, b: int, c: int): ...
@overload
@@ -217,7 +210,6 @@ def foo(a: float, b: float, c: bool): ...
def foo(a: bool, b: float, c: float): ...
def foo(a, b, c): ...
foo(Foo(), Foo()) # error: [no-matching-overload]
```
@@ -226,7 +218,6 @@ foo(Foo(), Foo()) # error: [no-matching-overload]
```py
from typing import overload
@overload
def f(
lion: int,
@@ -285,7 +276,6 @@ def f(
) -> int | str:
return 0
f(b"foo") # error: [no-matching-overload]
```
@@ -294,7 +284,6 @@ f(b"foo") # error: [no-matching-overload]
```py
from typing import overload
class Foo:
@overload
def bar(self, x: int) -> int: ...
@@ -303,7 +292,6 @@ class Foo:
def bar(self, x: int | str) -> int | str:
return x
foo = Foo()
foo.bar(b"wat") # error: [no-matching-overload]
```

View File

@@ -11,12 +11,10 @@ class A:
class B:
pass
class C:
class B:
pass
a: A.B = C.B() # error: [invalid-assignment] "Object of type `test.C.B` is not assignable to `test.A.B`"
```
@@ -28,7 +26,6 @@ a: A.B = C.B() # error: [invalid-assignment] "Object of type `test.C.B` is not
class B:
pass
def f(b: B):
class B:
pass
@@ -45,7 +42,6 @@ import b
df: a.DataFrame = b.DataFrame() # error: [invalid-assignment] "Object of type `b.DataFrame` is not assignable to `a.DataFrame`"
def _(dfs: list[b.DataFrame]):
# error: [invalid-assignment] "Object of type `list[b.DataFrame]` is not assignable to `list[a.DataFrame]`"
dataframes: list[a.DataFrame] = dfs
@@ -72,7 +68,6 @@ class DataFrame:
```py
from .foo import MyClass
def make_MyClass() -> MyClass:
return MyClass()
```
@@ -88,11 +83,10 @@ class MyClass: ...
```py
class MyClass: ...
def get_MyClass() -> MyClass:
from . import make_MyClass
# error: [invalid-return-type] "Return type does not match returned value: expected `package.foo.MyClass @ src/package/foo.py:1:7`, found `package.foo.MyClass @ src/package/foo.pyi:1:7`"
# error: [invalid-return-type] "Return type does not match returned value: expected `package.foo.MyClass @ src/package/foo.py:1`, found `package.foo.MyClass @ src/package/foo.pyi:1`"
return make_MyClass()
```
@@ -111,7 +105,6 @@ s: status_a.Status = status_b.Status.ACTIVE
```py
from enum import Enum
class Status(Enum):
ACTIVE = 1
INACTIVE = 2
@@ -122,7 +115,6 @@ class Status(Enum):
```py
from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
@@ -135,19 +127,16 @@ class Status(Enum):
```py
from enum import Enum
class A:
class B(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
class C:
class B(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
# error: [invalid-assignment] "Object of type `Literal[test.C.B.ACTIVE]` is not assignable to `test.A.B`"
a: A.B = C.B.ACTIVE
```
@@ -193,7 +182,6 @@ from typing import Generic, TypeVar
T = TypeVar("T")
class Container(Generic[T]):
pass
```
@@ -205,7 +193,6 @@ from typing import Generic, TypeVar
T = TypeVar("T")
class Container(Generic[T]):
pass
```
@@ -221,11 +208,9 @@ from typing import Protocol, TypeVar
T_co = TypeVar("T_co", covariant=True)
class Iterator(Protocol[T_co]):
def __nexxt__(self) -> T_co: ...
def bad() -> Iterator[str]:
raise NotImplementedError
```
@@ -235,7 +220,6 @@ def bad() -> Iterator[str]:
```py
from typing import Iterator
def f() -> Iterator[str]:
import bad
@@ -250,7 +234,6 @@ from typing import Protocol
import proto_a
import proto_b
def _(drawable_b: proto_b.Drawable):
# error: [invalid-assignment] "Object of type `proto_b.Drawable` is not assignable to `proto_a.Drawable`"
drawable: proto_a.Drawable = drawable_b
@@ -261,7 +244,6 @@ def _(drawable_b: proto_b.Drawable):
```py
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
```
@@ -271,7 +253,6 @@ class Drawable(Protocol):
```py
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> int: ...
```
@@ -283,7 +264,6 @@ from typing import TypedDict
import dict_a
import dict_b
def _(b_person: dict_b.Person):
# error: [invalid-assignment] "Object of type `dict_b.Person` is not assignable to `dict_a.Person`"
person_var: dict_a.Person = b_person
@@ -294,7 +274,6 @@ def _(b_person: dict_b.Person):
```py
from typing import TypedDict
class Person(TypedDict):
name: str
```
@@ -304,7 +283,6 @@ class Person(TypedDict):
```py
from typing import TypedDict
class Person(TypedDict):
name: bytes
```
@@ -320,7 +298,6 @@ class Model: ...
```py
class Model: ...
def get_models_tuple() -> tuple[Model]:
from module import Model

View File

@@ -18,7 +18,6 @@ python-version = "3.10"
async def elements(n):
yield n
async def f():
# error: 19 [invalid-syntax] "cannot use an asynchronous comprehension inside of a synchronous comprehension on Python 3.10 (syntax was added in 3.11)"
return {n: [x async for x in elements(n)] for n in range(3)}
@@ -39,11 +38,9 @@ correctly in the `SemanticSyntaxContext` trait:
async def f():
[x for x in [1]] and [x async for x in elements(1)]
async def f():
def g():
pass
[x async for x in elements(1)]
```
@@ -60,7 +57,6 @@ python-version = "3.11"
async def elements(n):
yield n
async def f():
return {n: [x async for x in elements(n)] for n in range(3)}
```
@@ -86,7 +82,6 @@ python-version = "3.12"
```py
from __future__ import annotations
# error: [invalid-type-form] "Named expressions are not allowed in type expressions"
# error: [invalid-syntax] "named expression cannot be used within a type annotation"
def f() -> (y := 3): ...
@@ -119,7 +114,6 @@ python-version = "3.10"
class Point:
pass
obj = Point()
match obj:
# error: [invalid-syntax] "attribute name `x` repeated in class pattern"
@@ -133,7 +127,6 @@ match obj:
class C:
def __await__(self): ...
# error: [invalid-syntax] "`return` statement outside of a function"
return
@@ -147,12 +140,10 @@ yield from []
# error: [invalid-syntax] "`await` outside of an asynchronous function"
await C()
def f():
# error: [invalid-syntax] "`await` outside of an asynchronous function"
await C()
(await cor async for cor in f()) # ok
(await cor for cor in f()) # ok
([await c for c in cor] async for cor in f()) # ok
@@ -164,7 +155,6 @@ Generators are evaluated lazily, so `await` is allowed, even outside of a functi
async def g():
yield 1
(x async for x in g())
```
@@ -219,7 +209,6 @@ python-version = "3.12"
class C[T, T]:
pass
# error: [invalid-syntax] "duplicate type parameter"
def f[X, Y, X]():
pass
@@ -234,12 +223,10 @@ def func():
# error: [invalid-syntax] "Starred expression cannot be used here"
return *[1, 2, 3]
def gen():
# error: [invalid-syntax] "Starred expression cannot be used here"
yield * [1, 2, 3]
# error: [invalid-syntax] "Starred expression cannot be used here"
for *x in range(10):
pass
@@ -300,12 +287,10 @@ python-version = "3.12"
# error: [invalid-syntax] "cannot assign to `__debug__`"
__debug__ = False
# error: [invalid-syntax] "cannot assign to `__debug__`"
def process(__debug__):
pass
# error: [invalid-syntax] "cannot assign to `__debug__`"
class Generic[__debug__]:
pass
@@ -326,19 +311,16 @@ def _():
# error: [invalid-syntax] "yield expression cannot be used within a TypeVar bound"
type X[T: (yield 1)] = int
def _():
# error: [invalid-type-form] "`yield` expressions are not allowed in type expressions"
# error: [invalid-syntax] "yield expression cannot be used within a type alias"
type Y = (yield 1)
# error: [invalid-type-form] "Named expressions are not allowed in type expressions"
# error: [invalid-syntax] "named expression cannot be used within a generic definition"
def f[T](x: int) -> (y := 3):
return x
def _():
# error: [invalid-syntax] "yield expression cannot be used within a generic definition"
class C[T]((yield from [object])):
@@ -353,7 +335,6 @@ This error includes `await`, `async for`, `async with`, and `async` comprehensio
async def elements(n):
yield n
def _():
# error: [invalid-syntax] "`await` outside of an asynchronous function"
await elements(1)
@@ -373,7 +354,6 @@ def _():
```py
x: int
def f():
x = 1
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
@@ -408,39 +388,32 @@ for x in range(42):
```py
a = None
def f(a):
global a # error: [invalid-syntax]
def g(a):
if True:
global a # error: [invalid-syntax]
def h(a):
def inner():
global a
def i(a):
try:
global a # error: [invalid-syntax]
except Exception:
pass
def f(a):
a = 1
global a # error: [invalid-syntax]
def f(a):
a = 1
a = 2
global a # error: [invalid-syntax]
def f(a):
class Inner:
global a # ok

View File

@@ -7,7 +7,6 @@
```py
class C: ...
C = 1 # error: [invalid-assignment]
```
@@ -16,6 +15,5 @@ C = 1 # error: [invalid-assignment]
```py
def f(): ...
f = 1 # error: [invalid-assignment]
```

View File

@@ -7,7 +7,6 @@ from typing_extensions import Any, Final, LiteralString, Self
X = Any
class Foo:
X: Final = LiteralString
a: int
@@ -17,7 +16,6 @@ class Foo:
def __init__(self):
self.y: Final = LiteralString
X.foo # error: [unresolved-attribute]
X.aaaaooooooo # error: [unresolved-attribute]
Foo.X.startswith # error: [unresolved-attribute]

View File

@@ -13,7 +13,6 @@ too verbose for it to be worth it.
def f(a, b=42): ...
def g(a, b): ...
class Foo:
def method(self, a): ...
```
@@ -25,11 +24,9 @@ from module import f, g, Foo
f(1, 2, 3) # error: [too-many-positional-arguments]
def coinflip() -> bool:
return True
h = f if coinflip() else g
# error: [too-many-positional-arguments]

View File

@@ -13,11 +13,9 @@ python-version = "3.12"
def f1() -> int:
return 0
def f2(name: str) -> int:
return 0
def _(flag: bool):
if flag:
f = f1
@@ -39,11 +37,9 @@ the end user.)
def f1(a: int) -> int:
return 0
def f2(name: str) -> int:
return 0
def _(flag: bool):
if flag:
f = f1
@@ -65,23 +61,18 @@ just ensuring that we get test coverage for each of the possible diagnostic mess
from inspect import getattr_static
from typing import overload
def f1() -> int:
return 0
def f2(name: str) -> int:
return 0
def f3(a: int, b: int) -> int:
return 0
def f4[T: str](x: T) -> int:
return 0
@overload
def f5() -> None: ...
@overload
@@ -89,7 +80,6 @@ def f5(x: str) -> str: ...
def f5(x: str | None = None) -> str | None:
return x
@overload
def f6() -> None: ...
@overload
@@ -97,11 +87,9 @@ def f6(x: str, y: str) -> str: ...
def f6(x: str | None = None, y: str | None = None) -> str | None:
return x + y if x and y else None
def _(n: int):
class PossiblyNotCallable:
if n == 0:
def __call__(self) -> int:
return 0
@@ -138,11 +126,9 @@ def _(n: int):
def any(*args, **kwargs) -> int:
return 0
def f1(name: str) -> int:
return 0
def _(n: int):
if n == 0:
f = f1
@@ -161,29 +147,16 @@ therefore truncate the long expected union type to avoid overwhelming output.
```py
from typing import Literal, Union
class A: ...
class B: ...
class C: ...
class D: ...
class E: ...
class F: ...
def f1(x: Union[Literal[1, 2, 3, 4, 5, 6, 7, 8], A, B, C, D, E, F]) -> int:
return 0
def _(n: int):
x = n
# error: [invalid-argument-type]

View File

@@ -13,7 +13,6 @@ sub-diagnostic for each element would probably be too verbose for it to be worth
def f(a, b, c=42): ...
def g(a, b): ...
class Foo:
def method(self, a, b): ...
```
@@ -25,11 +24,9 @@ from module import f, g, Foo
f(a=1, b=2, c=3, d=42) # error: [unknown-argument]
def coinflip() -> bool:
return True
h = f if coinflip() else g
# error: [unknown-argument]

View File

@@ -9,7 +9,6 @@ class NotBoolable:
def __bool__(self, foo):
return False
a = NotBoolable()
# error: [unsupported-bool-conversion]
@@ -23,7 +22,6 @@ class NotBoolable:
def __bool__(self) -> str:
return "wat"
a = NotBoolable()
# error: [unsupported-bool-conversion]
@@ -36,7 +34,6 @@ a = NotBoolable()
class NotBoolable:
__bool__: int = 3
a = NotBoolable()
# error: [unsupported-bool-conversion]
@@ -50,19 +47,15 @@ class NotBoolable1:
def __bool__(self) -> str:
return "wat"
class NotBoolable2:
pass
class NotBoolable3:
__bool__: int = 3
def get() -> NotBoolable1 | NotBoolable2 | NotBoolable3:
return NotBoolable2()
# error: [unsupported-bool-conversion]
10 and get() and True
```

View File

@@ -10,7 +10,6 @@
from typing_extensions import assert_never, Never, Any
from ty_extensions import Unknown
def _(never: Never):
assert_never(never) # fine
```
@@ -25,31 +24,24 @@ If it is not, a `type-assertion-failure` diagnostic is emitted.
from typing_extensions import assert_never, Never, Any
from ty_extensions import Unknown
def _():
assert_never(0) # error: [type-assertion-failure]
def _():
assert_never("") # error: [type-assertion-failure]
def _():
assert_never(None) # error: [type-assertion-failure]
def _():
assert_never(()) # error: [type-assertion-failure]
def _(flag: bool, never: Never):
assert_never(1 if flag else never) # error: [type-assertion-failure]
def _(any_: Any):
assert_never(any_) # error: [type-assertion-failure]
def _(unknown: Unknown):
assert_never(unknown) # error: [type-assertion-failure]
```
@@ -67,16 +59,10 @@ are handled in a series of `isinstance` checks or other narrowing patterns that
```py
from typing_extensions import assert_never, Literal
class A: ...
class B: ...
class C: ...
def if_else_isinstance_success(obj: A | B):
if isinstance(obj, A):
pass
@@ -87,7 +73,6 @@ def if_else_isinstance_success(obj: A | B):
else:
assert_never(obj)
def if_else_isinstance_error(obj: A | B):
if isinstance(obj, A):
pass
@@ -98,7 +83,6 @@ def if_else_isinstance_error(obj: A | B):
# error: [type-assertion-failure] "Type `B & ~A & ~C` is not equivalent to `Never`"
assert_never(obj)
def if_else_singletons_success(obj: Literal[1, "a"] | None):
if obj == 1:
pass
@@ -109,7 +93,6 @@ def if_else_singletons_success(obj: Literal[1, "a"] | None):
else:
assert_never(obj)
def if_else_singletons_error(obj: Literal[1, "a"] | None):
if obj == 1:
pass
@@ -121,7 +104,6 @@ def if_else_singletons_error(obj: Literal[1, "a"] | None):
# error: [type-assertion-failure] "Type `Literal["a"]` is not equivalent to `Never`"
assert_never(obj)
def match_singletons_success(obj: Literal[1, "a"] | None):
match obj:
case 1:
@@ -133,7 +115,6 @@ def match_singletons_success(obj: Literal[1, "a"] | None):
case _ as obj:
assert_never(obj)
def match_singletons_error(obj: Literal[1, "a"] | None):
match obj:
case 1:

Some files were not shown because too many files have changed in this diff Show More