Compare commits
1 Commits
micha/mdte
...
jack/loop-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a38a18e2d3 |
@@ -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)^(
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
207
crates/ty/docs/rules.md
generated
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'>)
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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), ...]
|
||||
```
|
||||
|
||||
@@ -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)
|
||||
```
|
||||
|
||||
@@ -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: """
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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): ...
|
||||
```
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]'>
|
||||
|
||||
@@ -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()
|
||||
```
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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, ...]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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))
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
```
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
```py
|
||||
class A: ...
|
||||
|
||||
|
||||
def _(a1: A, a2: A, o: object):
|
||||
n1 = None
|
||||
n2 = None
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"]]
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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():
|
||||
...
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
```
|
||||
|
||||
@@ -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'>
|
||||
```
|
||||
|
||||
@@ -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))
|
||||
```
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user