Compare commits
28 Commits
david/type
...
0.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e6de4e0c6 | ||
|
|
70e5c4a8ba | ||
|
|
9218d6bedc | ||
|
|
1b79ae9817 | ||
|
|
2b87587ac2 | ||
|
|
d1e15f6246 | ||
|
|
89a82158a1 | ||
|
|
202c6a6d75 | ||
|
|
5c3c0c4705 | ||
|
|
6b7a738825 | ||
|
|
4ea4bbb155 | ||
|
|
ed4a0b34ba | ||
|
|
2095ea8372 | ||
|
|
6282402a8c | ||
|
|
d25673f664 | ||
|
|
a94914dc35 | ||
|
|
2ffc3fad47 | ||
|
|
8f5b2aac9a | ||
|
|
b85be6297e | ||
|
|
fb1d1e3241 | ||
|
|
c6b311c546 | ||
|
|
b16f665a81 | ||
|
|
d77480768d | ||
|
|
5fa82fb0cd | ||
|
|
74bf4b0653 | ||
|
|
5f65e842e8 | ||
|
|
72ac6cd5a5 | ||
|
|
04b636cba2 |
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,5 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
## 0.7.0
|
||||
|
||||
Check out the [blog post](https://astral.sh/blog/ruff-v0.7.0) for a migration guide and overview of the changes!
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- The pytest rules `PT001` and `PT023` now default to omitting the decorator parentheses when there are no arguments
|
||||
([#12838](https://github.com/astral-sh/ruff/pull/12838), [#13292](https://github.com/astral-sh/ruff/pull/13292)).
|
||||
This was a change that we attempted to make in Ruff v0.6.0, but only partially made due to an error on our part.
|
||||
See the [blog post](https://astral.sh/blog/ruff-v0.7.0) for more details.
|
||||
- The `useless-try-except` rule (in our `tryceratops` category) has been recoded from `TRY302` to
|
||||
`TRY203` ([#13502](https://github.com/astral-sh/ruff/pull/13502)). This ensures Ruff's code is consistent with
|
||||
the same rule in the [`tryceratops`](https://github.com/guilatrova/tryceratops) linter.
|
||||
- The `lint.allow-unused-imports` setting has been removed ([#13677](https://github.com/astral-sh/ruff/pull/13677)). Use
|
||||
[`lint.pyflakes.allow-unused-imports`](https://docs.astral.sh/ruff/settings/#lint_pyflakes_allowed-unused-imports)
|
||||
instead.
|
||||
|
||||
### Formatter preview style
|
||||
|
||||
- Normalize implicit concatenated f-string quotes per part ([#13539](https://github.com/astral-sh/ruff/pull/13539))
|
||||
|
||||
### Preview linter features
|
||||
|
||||
- \[`refurb`\] implement `hardcoded-string-charset` (FURB156) ([#13530](https://github.com/astral-sh/ruff/pull/13530))
|
||||
- \[`refurb`\] Count codepoints not bytes for `slice-to-remove-prefix-or-suffix (FURB188)` ([#13631](https://github.com/astral-sh/ruff/pull/13631))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`pylint`\] Mark `PLE1141` fix as unsafe ([#13629](https://github.com/astral-sh/ruff/pull/13629))
|
||||
- \[`flake8-async`\] Consider async generators to be "checkpoints" for `cancel-scope-no-checkpoint` (`ASYNC100`) ([#13639](https://github.com/astral-sh/ruff/pull/13639))
|
||||
- \[`flake8-bugbear`\] Do not suggest setting parameter `strict=` to `False` in `B905` diagnostic message ([#13656](https://github.com/astral-sh/ruff/pull/13656))
|
||||
- \[`flake8-todos`\] Only flag the word "TODO", not words starting with "todo" (`TD006`) ([#13640](https://github.com/astral-sh/ruff/pull/13640))
|
||||
- \[`pycodestyle`\] Fix whitespace-related false positives and false negatives inside type-parameter lists (`E231`, `E251`) ([#13704](https://github.com/astral-sh/ruff/pull/13704))
|
||||
- \[`flake8-simplify`\] Stabilize preview behavior for `SIM115` so that the rule can detect files
|
||||
being opened from a wider range of standard-library functions ([#12959](https://github.com/astral-sh/ruff/pull/12959)).
|
||||
|
||||
### CLI
|
||||
|
||||
- Add explanation of fixable in `--statistics` command ([#13774](https://github.com/astral-sh/ruff/pull/13774))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`pyflakes`\] Allow `ipytest` cell magic (`F401`) ([#13745](https://github.com/astral-sh/ruff/pull/13745))
|
||||
- \[`flake8-use-pathlib`\] Fix `PTH123` false positive when `open` is passed a file descriptor ([#13616](https://github.com/astral-sh/ruff/pull/13616))
|
||||
- \[`flake8-bandit`\] Detect patterns from multi line SQL statements (`S608`) ([#13574](https://github.com/astral-sh/ruff/pull/13574))
|
||||
- \[`flake8-pyi`\] - Fix dropped expressions in `PYI030` autofix ([#13727](https://github.com/astral-sh/ruff/pull/13727))
|
||||
|
||||
## 0.6.9
|
||||
|
||||
### Preview features
|
||||
|
||||
13
Cargo.lock
generated
13
Cargo.lock
generated
@@ -2083,6 +2083,7 @@ dependencies = [
|
||||
"hashbrown 0.15.0",
|
||||
"insta",
|
||||
"itertools 0.13.0",
|
||||
"memchr",
|
||||
"ordermap",
|
||||
"red_knot_test",
|
||||
"red_knot_vendored",
|
||||
@@ -2319,7 +2320,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2538,7 +2539,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2858,7 +2859,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
@@ -3008,7 +3009,7 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
[[package]]
|
||||
name = "salsa"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=4a7c955255e707e64e43f3ce5eabb771ae067768#4a7c955255e707e64e43f3ce5eabb771ae067768"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=b14be5c0392f4c55eca60b92e457a35549372382#b14be5c0392f4c55eca60b92e457a35549372382"
|
||||
dependencies = [
|
||||
"append-only-vec",
|
||||
"arc-swap",
|
||||
@@ -3028,12 +3029,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "salsa-macro-rules"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=4a7c955255e707e64e43f3ce5eabb771ae067768#4a7c955255e707e64e43f3ce5eabb771ae067768"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=b14be5c0392f4c55eca60b92e457a35549372382#b14be5c0392f4c55eca60b92e457a35549372382"
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macros"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=4a7c955255e707e64e43f3ce5eabb771ae067768#4a7c955255e707e64e43f3ce5eabb771ae067768"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=b14be5c0392f4c55eca60b92e457a35549372382#b14be5c0392f4c55eca60b92e457a35549372382"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -117,7 +117,7 @@ rayon = { version = "1.10.0" }
|
||||
regex = { version = "1.10.2" }
|
||||
rstest = { version = "0.22.0", default-features = false }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "4a7c955255e707e64e43f3ce5eabb771ae067768" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "b14be5c0392f4c55eca60b92e457a35549372382" }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
|
||||
@@ -136,8 +136,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.6.9/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.6.9/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.7.0/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.7.0/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -170,7 +170,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.6.9
|
||||
rev: v0.7.0
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -501,7 +501,10 @@ fn directory_moved_to_workspace() -> anyhow::Result<()> {
|
||||
.with_context(|| "Failed to create __init__.py")?;
|
||||
std::fs::write(a_original_path.as_std_path(), "").with_context(|| "Failed to create a.py")?;
|
||||
|
||||
let sub_a_module = resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap());
|
||||
let sub_a_module = resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("sub.a").unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(sub_a_module, None);
|
||||
assert_eq!(
|
||||
@@ -525,7 +528,11 @@ fn directory_moved_to_workspace() -> anyhow::Result<()> {
|
||||
.expect("a.py to exist");
|
||||
|
||||
// `import sub.a` should now resolve
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("sub.a").unwrap()
|
||||
)
|
||||
.is_some());
|
||||
|
||||
assert_eq!(
|
||||
case.collect_package_files(&case.workspace_path("bar.py")),
|
||||
@@ -544,7 +551,11 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
|
||||
])?;
|
||||
let bar = case.system_file(case.workspace_path("bar.py")).unwrap();
|
||||
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("sub.a").unwrap()
|
||||
)
|
||||
.is_some());
|
||||
|
||||
let sub_path = case.workspace_path("sub");
|
||||
let init_file = case
|
||||
@@ -569,7 +580,11 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
|
||||
case.apply_changes(changes);
|
||||
|
||||
// `import sub.a` should no longer resolve
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_none());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("sub.a").unwrap()
|
||||
)
|
||||
.is_none());
|
||||
|
||||
assert!(!init_file.exists(case.db()));
|
||||
assert!(!a_file.exists(case.db()));
|
||||
@@ -592,10 +607,14 @@ fn directory_renamed() -> anyhow::Result<()> {
|
||||
|
||||
let bar = case.system_file(case.workspace_path("bar.py")).unwrap();
|
||||
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
ModuleName::new_static("foo.baz").unwrap()
|
||||
&ModuleName::new_static("sub.a").unwrap()
|
||||
)
|
||||
.is_some());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("foo.baz").unwrap()
|
||||
)
|
||||
.is_none());
|
||||
|
||||
@@ -623,11 +642,15 @@ fn directory_renamed() -> anyhow::Result<()> {
|
||||
case.apply_changes(changes);
|
||||
|
||||
// `import sub.a` should no longer resolve
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_none());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("sub.a").unwrap()
|
||||
)
|
||||
.is_none());
|
||||
// `import foo.baz` should now resolve
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
ModuleName::new_static("foo.baz").unwrap()
|
||||
&ModuleName::new_static("foo.baz").unwrap()
|
||||
)
|
||||
.is_some());
|
||||
|
||||
@@ -665,7 +688,11 @@ fn directory_deleted() -> anyhow::Result<()> {
|
||||
|
||||
let bar = case.system_file(case.workspace_path("bar.py")).unwrap();
|
||||
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("sub.a").unwrap()
|
||||
)
|
||||
.is_some());
|
||||
|
||||
let sub_path = case.workspace_path("sub");
|
||||
|
||||
@@ -688,7 +715,11 @@ fn directory_deleted() -> anyhow::Result<()> {
|
||||
case.apply_changes(changes);
|
||||
|
||||
// `import sub.a` should no longer resolve
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_none());
|
||||
assert!(resolve_module(
|
||||
case.db().upcast(),
|
||||
&ModuleName::new_static("sub.a").unwrap()
|
||||
)
|
||||
.is_none());
|
||||
|
||||
assert!(!init_file.exists(case.db()));
|
||||
assert!(!a_file.exists(case.db()));
|
||||
@@ -710,7 +741,7 @@ fn search_path() -> anyhow::Result<()> {
|
||||
let site_packages = case.root_path().join("site_packages");
|
||||
|
||||
assert_eq!(
|
||||
resolve_module(case.db(), ModuleName::new("a").unwrap()),
|
||||
resolve_module(case.db(), &ModuleName::new("a").unwrap()),
|
||||
None
|
||||
);
|
||||
|
||||
@@ -720,7 +751,7 @@ fn search_path() -> anyhow::Result<()> {
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("a").unwrap()).is_some());
|
||||
assert!(resolve_module(case.db().upcast(), &ModuleName::new_static("a").unwrap()).is_some());
|
||||
assert_eq!(
|
||||
case.collect_package_files(&case.workspace_path("bar.py")),
|
||||
&[case.system_file(case.workspace_path("bar.py")).unwrap()]
|
||||
@@ -736,7 +767,7 @@ fn add_search_path() -> anyhow::Result<()> {
|
||||
let site_packages = case.workspace_path("site_packages");
|
||||
std::fs::create_dir_all(site_packages.as_std_path())?;
|
||||
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("a").unwrap()).is_none());
|
||||
assert!(resolve_module(case.db().upcast(), &ModuleName::new_static("a").unwrap()).is_none());
|
||||
|
||||
// Register site-packages as a search path.
|
||||
case.update_search_path_settings(SearchPathConfiguration {
|
||||
@@ -751,7 +782,7 @@ fn add_search_path() -> anyhow::Result<()> {
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("a").unwrap()).is_some());
|
||||
assert!(resolve_module(case.db().upcast(), &ModuleName::new_static("a").unwrap()).is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -805,7 +836,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
|
||||
|
||||
// Unset the custom typeshed directory.
|
||||
assert_eq!(
|
||||
resolve_module(case.db(), ModuleName::new("os").unwrap()),
|
||||
resolve_module(case.db(), &ModuleName::new("os").unwrap()),
|
||||
None
|
||||
);
|
||||
|
||||
@@ -820,7 +851,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
assert!(resolve_module(case.db(), ModuleName::new("os").unwrap()).is_some());
|
||||
assert!(resolve_module(case.db(), &ModuleName::new("os").unwrap()).is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1044,7 +1075,7 @@ mod unix {
|
||||
|
||||
let baz = resolve_module(
|
||||
case.db().upcast(),
|
||||
ModuleName::new_static("bar.baz").unwrap(),
|
||||
&ModuleName::new_static("bar.baz").unwrap(),
|
||||
)
|
||||
.expect("Expected bar.baz to exist in site-packages.");
|
||||
let baz_workspace = case.workspace_path("bar/baz.py");
|
||||
@@ -1125,7 +1156,7 @@ mod unix {
|
||||
|
||||
let baz = resolve_module(
|
||||
case.db().upcast(),
|
||||
ModuleName::new_static("bar.baz").unwrap(),
|
||||
&ModuleName::new_static("bar.baz").unwrap(),
|
||||
)
|
||||
.expect("Expected bar.baz to exist in site-packages.");
|
||||
let bar_baz = case.workspace_path("bar/baz.py");
|
||||
@@ -1229,7 +1260,7 @@ mod unix {
|
||||
|
||||
let baz = resolve_module(
|
||||
case.db().upcast(),
|
||||
ModuleName::new_static("bar.baz").unwrap(),
|
||||
&ModuleName::new_static("bar.baz").unwrap(),
|
||||
)
|
||||
.expect("Expected bar.baz to exist in site-packages.");
|
||||
let baz_site_packages_path =
|
||||
|
||||
@@ -34,6 +34,7 @@ hashbrown = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
test-case = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ruff_db = { workspace = true, features = ["os", "testing"] }
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Assignment with annotations
|
||||
|
||||
## Annotation only transparent to local inference
|
||||
|
||||
```py
|
||||
x = 1
|
||||
x: int
|
||||
y = x
|
||||
|
||||
reveal_type(y) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
## Violates own annotation
|
||||
|
||||
```py
|
||||
x: int = 'foo' # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`"
|
||||
|
||||
```
|
||||
|
||||
## Violates previous annotation
|
||||
|
||||
```py
|
||||
x: int
|
||||
x = 'foo' # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`"
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
# Multi-target assignment
|
||||
|
||||
## Basic
|
||||
|
||||
```py
|
||||
x = y = 1
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
reveal_type(y) # revealed: Literal[1]
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
# Unbound
|
||||
|
||||
## Unbound
|
||||
|
||||
```py
|
||||
x = foo
|
||||
foo = 1
|
||||
reveal_type(x) # revealed: Unbound
|
||||
```
|
||||
|
||||
## Unbound class variable
|
||||
|
||||
Name lookups within a class scope fall back to globals, but lookups of class attributes don't.
|
||||
|
||||
```py
|
||||
x = 1
|
||||
class C:
|
||||
y = x
|
||||
if flag:
|
||||
x = 2
|
||||
|
||||
reveal_type(C.x) # revealed: Literal[2]
|
||||
reveal_type(C.y) # revealed: Literal[1]
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
# Walrus operator
|
||||
|
||||
## Basic
|
||||
|
||||
```py
|
||||
x = (y := 1) + 1
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
reveal_type(y) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
## Walrus self-addition
|
||||
|
||||
```py
|
||||
x = 0
|
||||
(x := x + 1)
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# Class attributes
|
||||
|
||||
## Union of attributes
|
||||
|
||||
```py
|
||||
if flag:
|
||||
class C:
|
||||
x = 1
|
||||
else:
|
||||
class C:
|
||||
x = 2
|
||||
|
||||
y = C.x
|
||||
reveal_type(y) # revealed: Literal[1, 2]
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
## Binary operations on integers
|
||||
|
||||
## Basic Arithmetic
|
||||
|
||||
```py
|
||||
a = 2 + 1
|
||||
b = a - 4
|
||||
c = a * b
|
||||
d = c // 3
|
||||
e = c / 3
|
||||
f = 5 % 3
|
||||
|
||||
reveal_type(a) # revealed: Literal[3]
|
||||
reveal_type(b) # revealed: Literal[-1]
|
||||
reveal_type(c) # revealed: Literal[-3]
|
||||
reveal_type(d) # revealed: Literal[-1]
|
||||
reveal_type(e) # revealed: float
|
||||
reveal_type(f) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
## Division by Zero
|
||||
|
||||
```py
|
||||
# TODO: `a` should be `int` and `e` should be `float` once we support inference.
|
||||
a = 1 / 0 # error: "Cannot divide object of type `Literal[1]` by zero"
|
||||
b = 2 // 0 # error: "Cannot floor divide object of type `Literal[2]` by zero"
|
||||
c = 3 % 0 # error: "Cannot reduce object of type `Literal[3]` modulo zero"
|
||||
d = int() / 0 # error: "Cannot divide object of type `int` by zero"
|
||||
e = 1.0 / 0 # error: "Cannot divide object of type `float` by zero"
|
||||
|
||||
reveal_type(a) # revealed: float
|
||||
reveal_type(b) # revealed: int
|
||||
reveal_type(c) # revealed: int
|
||||
reveal_type(d) # revealed: @Todo
|
||||
reveal_type(e) # revealed: @Todo
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
# Callable instance
|
||||
|
||||
## Dunder call
|
||||
|
||||
```py
|
||||
class Multiplier:
|
||||
def __init__(self, factor: float):
|
||||
self.factor = factor
|
||||
|
||||
def __call__(self, number: float) -> float:
|
||||
return number * self.factor
|
||||
|
||||
a = Multiplier(2.0)(3.0)
|
||||
|
||||
class Unit: ...
|
||||
|
||||
b = Unit()(3.0) # error: "Object of type `Unit` is not callable"
|
||||
|
||||
reveal_type(a) # revealed: float
|
||||
reveal_type(b) # revealed: Unknown
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
# Constructor
|
||||
|
||||
```py
|
||||
class Foo: ...
|
||||
|
||||
x = Foo()
|
||||
reveal_type(x) # revealed: Foo
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
# Call expression
|
||||
|
||||
## Simple
|
||||
|
||||
```py
|
||||
def get_int() -> int:
|
||||
return 42
|
||||
|
||||
x = get_int()
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
## Async
|
||||
|
||||
```py
|
||||
async def get_int_async() -> int:
|
||||
return 42
|
||||
|
||||
x = get_int_async()
|
||||
|
||||
# TODO: we don't yet support `types.CoroutineType`, should be generic `Coroutine[Any, Any, int]`
|
||||
reveal_type(x) # revealed: @Todo
|
||||
```
|
||||
|
||||
## Decorated
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
|
||||
def foo() -> int:
|
||||
return 42
|
||||
|
||||
def decorator(func) -> Callable[[], int]:
|
||||
return foo
|
||||
|
||||
@decorator
|
||||
def bar() -> str:
|
||||
return 'bar'
|
||||
|
||||
x = bar()
|
||||
|
||||
# TODO: should reveal `int`, as the decorator replaces `bar` with `foo`
|
||||
reveal_type(x) # revealed: @Todo
|
||||
```
|
||||
|
||||
## Invalid callable
|
||||
|
||||
```py
|
||||
nonsense = 123
|
||||
x = nonsense() # error: "Object of type `Literal[123]` is not callable"
|
||||
```
|
||||
@@ -0,0 +1,74 @@
|
||||
# Unions in calls
|
||||
|
||||
## Union of return types
|
||||
|
||||
```py
|
||||
if flag:
|
||||
def f() -> int:
|
||||
return 1
|
||||
else:
|
||||
def f() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = f()
|
||||
reveal_type(x) # revealed: int | str
|
||||
```
|
||||
|
||||
## Calling with an unknown union
|
||||
|
||||
```py
|
||||
from nonexistent import f # error: [unresolved-import] "Cannot resolve import `nonexistent`"
|
||||
|
||||
if flag:
|
||||
def f() -> int:
|
||||
return 1
|
||||
|
||||
x = f()
|
||||
reveal_type(x) # revealed: Unknown | int
|
||||
```
|
||||
|
||||
## Non-callable elements in a union
|
||||
|
||||
Calling a union with a non-callable element should emit a diagnostic.
|
||||
|
||||
```py
|
||||
if flag:
|
||||
f = 1
|
||||
else:
|
||||
def f() -> int:
|
||||
return 1
|
||||
|
||||
x = f() # error: "Object of type `Literal[1] | Literal[f]` is not callable (due to union element `Literal[1]`)"
|
||||
reveal_type(x) # revealed: Unknown | int
|
||||
```
|
||||
|
||||
## Multiple non-callable elements in a union
|
||||
|
||||
Calling a union with multiple non-callable elements should mention all of them in the diagnostic.
|
||||
|
||||
```py
|
||||
if flag:
|
||||
f = 1
|
||||
elif flag2:
|
||||
f = 'foo'
|
||||
else:
|
||||
def f() -> int:
|
||||
return 1
|
||||
|
||||
x = f() # error: "Object of type `Literal[1] | Literal["foo"] | Literal[f]` is not callable (due to union elements Literal[1], Literal["foo"])"
|
||||
reveal_type(x) # revealed: Unknown | int
|
||||
```
|
||||
|
||||
## All non-callable union elements
|
||||
|
||||
Calling a union with no callable elements can emit a simpler diagnostic.
|
||||
|
||||
```py
|
||||
if flag:
|
||||
f = 1
|
||||
else:
|
||||
f = 'foo'
|
||||
|
||||
x = f() # error: "Object of type `Literal[1] | Literal["foo"]` is not callable"
|
||||
reveal_type(x) # revealed: Unknown
|
||||
```
|
||||
@@ -0,0 +1,41 @@
|
||||
# Comparing integers
|
||||
|
||||
## Integer literals
|
||||
|
||||
```py
|
||||
a = 1 == 1 == True
|
||||
b = 1 == 1 == 2 == 4
|
||||
c = False < True <= 2 < 3 != 6
|
||||
d = 1 < 1
|
||||
e = 1 > 1
|
||||
f = 1 is 1
|
||||
g = 1 is not 1
|
||||
h = 1 is 2
|
||||
i = 1 is not 7
|
||||
j = 1 <= "" and 0 < 1
|
||||
|
||||
reveal_type(a) # revealed: Literal[True]
|
||||
reveal_type(b) # revealed: Literal[False]
|
||||
reveal_type(c) # revealed: Literal[True]
|
||||
reveal_type(d) # revealed: Literal[False]
|
||||
reveal_type(e) # revealed: Literal[False]
|
||||
reveal_type(f) # revealed: bool
|
||||
reveal_type(g) # revealed: bool
|
||||
reveal_type(h) # revealed: Literal[False]
|
||||
reveal_type(i) # revealed: Literal[True]
|
||||
reveal_type(j) # revealed: @Todo | Literal[True]
|
||||
```
|
||||
|
||||
## Integer instance
|
||||
|
||||
```py
|
||||
# TODO: implement lookup of `__eq__` on typeshed `int` stub.
|
||||
def int_instance() -> int: ...
|
||||
a = 1 == int_instance()
|
||||
b = 9 < int_instance()
|
||||
c = int_instance() < int_instance()
|
||||
|
||||
reveal_type(a) # revealed: @Todo
|
||||
reveal_type(b) # revealed: bool
|
||||
reveal_type(c) # revealed: bool
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# Non boolean returns
|
||||
|
||||
Walking through examples:
|
||||
|
||||
- `a = A() < B() < C()`
|
||||
|
||||
1. `A() < B() and B() < C()` - split in N comparison
|
||||
1. `A()` and `B()` - evaluate outcome types
|
||||
1. `bool` and `bool` - evaluate truthiness
|
||||
1. `A | B` - union of "first true" types
|
||||
|
||||
- `b = 0 < 1 < A() < 3`
|
||||
|
||||
1. `0 < 1 and 1 < A() and A() < 3` - split in N comparison
|
||||
1. `True` and `bool` and `A` - evaluate outcome types
|
||||
1. `True` and `bool` and `bool` - evaluate truthiness
|
||||
1. `bool | A` - union of "true" types
|
||||
|
||||
- `c = 10 < 0 < A() < B() < C()` short-circuit to False
|
||||
|
||||
```py
|
||||
from __future__ import annotations
|
||||
class A:
|
||||
def __lt__(self, other) -> A: ...
|
||||
class B:
|
||||
def __lt__(self, other) -> B: ...
|
||||
class C:
|
||||
def __lt__(self, other) -> C: ...
|
||||
|
||||
a = A() < B() < C()
|
||||
b = 0 < 1 < A() < 3
|
||||
c = 10 < 0 < A() < B() < C()
|
||||
|
||||
reveal_type(a) # revealed: A | B
|
||||
reveal_type(b) # revealed: bool | A
|
||||
reveal_type(c) # revealed: Literal[False]
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
# Comparing strings
|
||||
|
||||
## String literals
|
||||
|
||||
```py
|
||||
def str_instance() -> str: ...
|
||||
a = "abc" == "abc"
|
||||
b = "ab_cd" <= "ab_ce"
|
||||
c = "abc" in "ab cd"
|
||||
d = "" not in "hello"
|
||||
e = "--" is "--"
|
||||
f = "A" is "B"
|
||||
g = "--" is not "--"
|
||||
h = "A" is not "B"
|
||||
i = str_instance() < "..."
|
||||
# ensure we're not comparing the interned salsa symbols, which compare by order of declaration.
|
||||
j = "ab" < "ab_cd"
|
||||
|
||||
reveal_type(a) # revealed: Literal[True]
|
||||
reveal_type(b) # revealed: Literal[True]
|
||||
reveal_type(c) # revealed: Literal[False]
|
||||
reveal_type(d) # revealed: Literal[False]
|
||||
reveal_type(e) # revealed: bool
|
||||
reveal_type(f) # revealed: Literal[False]
|
||||
reveal_type(g) # revealed: bool
|
||||
reveal_type(h) # revealed: Literal[True]
|
||||
reveal_type(i) # revealed: bool
|
||||
reveal_type(j) # revealed: Literal[True]
|
||||
```
|
||||
@@ -0,0 +1,205 @@
|
||||
# Comparison - Tuples
|
||||
|
||||
## Heterogeneous
|
||||
|
||||
For tuples like `tuple[int, str, Literal[1]]`
|
||||
|
||||
### Value Comparisons
|
||||
|
||||
"Value Comparisons" refers to the operators: `==`, `!=`, `<`, `<=`, `>`, `>=`
|
||||
|
||||
#### Results without Ambiguity
|
||||
|
||||
Cases where the result can be definitively inferred as a `BooleanLiteral`.
|
||||
|
||||
```py
|
||||
a = (1, "test", (3, 13), True)
|
||||
b = (1, "test", (3, 14), False)
|
||||
|
||||
reveal_type(a == a) # revealed: Literal[True]
|
||||
reveal_type(a != a) # revealed: Literal[False]
|
||||
reveal_type(a < a) # revealed: Literal[False]
|
||||
reveal_type(a <= a) # revealed: Literal[True]
|
||||
reveal_type(a > a) # revealed: Literal[False]
|
||||
reveal_type(a >= a) # revealed: Literal[True]
|
||||
|
||||
reveal_type(a == b) # revealed: Literal[False]
|
||||
reveal_type(a != b) # revealed: Literal[True]
|
||||
reveal_type(a < b) # revealed: Literal[True]
|
||||
reveal_type(a <= b) # revealed: Literal[True]
|
||||
reveal_type(a > b) # revealed: Literal[False]
|
||||
reveal_type(a >= b) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
Even when tuples have different lengths, comparisons should be handled appropriately.
|
||||
|
||||
```py path=different_length.py
|
||||
a = (1, 2, 3)
|
||||
b = (1, 2, 3, 4)
|
||||
|
||||
reveal_type(a == b) # revealed: Literal[False]
|
||||
reveal_type(a != b) # revealed: Literal[True]
|
||||
reveal_type(a < b) # revealed: Literal[True]
|
||||
reveal_type(a <= b) # revealed: Literal[True]
|
||||
reveal_type(a > b) # revealed: Literal[False]
|
||||
reveal_type(a >= b) # revealed: Literal[False]
|
||||
|
||||
c = ("a", "b", "c", "d")
|
||||
d = ("a", "b", "c")
|
||||
|
||||
reveal_type(c == d) # revealed: Literal[False]
|
||||
reveal_type(c != d) # revealed: Literal[True]
|
||||
reveal_type(c < d) # revealed: Literal[False]
|
||||
reveal_type(c <= d) # revealed: Literal[False]
|
||||
reveal_type(c > d) # revealed: Literal[True]
|
||||
reveal_type(c >= d) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
#### Results with Ambiguity
|
||||
|
||||
```py
|
||||
def bool_instance() -> bool: ...
|
||||
def int_instance() -> int: ...
|
||||
|
||||
a = (bool_instance(),)
|
||||
b = (int_instance(),)
|
||||
|
||||
# TODO: All @Todo should be `bool`
|
||||
reveal_type(a == a) # revealed: @Todo
|
||||
reveal_type(a != a) # revealed: @Todo
|
||||
reveal_type(a < a) # revealed: @Todo
|
||||
reveal_type(a <= a) # revealed: @Todo
|
||||
reveal_type(a > a) # revealed: @Todo
|
||||
reveal_type(a >= a) # revealed: @Todo
|
||||
|
||||
reveal_type(a == b) # revealed: @Todo
|
||||
reveal_type(a != b) # revealed: @Todo
|
||||
reveal_type(a < b) # revealed: @Todo
|
||||
reveal_type(a <= b) # revealed: @Todo
|
||||
reveal_type(a > b) # revealed: @Todo
|
||||
reveal_type(a >= b) # revealed: @Todo
|
||||
```
|
||||
|
||||
#### Comparison Unsupported
|
||||
|
||||
If two tuples contain types that do not support comparison, the result may be `Unknown`.
|
||||
However, `==` and `!=` are exceptions and can still provide definite results.
|
||||
|
||||
```py
|
||||
a = (1, 2)
|
||||
b = (1, "hello")
|
||||
|
||||
# TODO: should be Literal[False]
|
||||
reveal_type(a == b) # revealed: @Todo
|
||||
|
||||
# TODO: should be Literal[True]
|
||||
reveal_type(a != b) # revealed: @Todo
|
||||
|
||||
# TODO: should be Unknown and add more informative diagnostics
|
||||
reveal_type(a < b) # revealed: @Todo
|
||||
reveal_type(a <= b) # revealed: @Todo
|
||||
reveal_type(a > b) # revealed: @Todo
|
||||
reveal_type(a >= b) # revealed: @Todo
|
||||
```
|
||||
|
||||
However, if the lexicographic comparison completes without reaching a point where str and int are compared,
|
||||
Python will still produce a result based on the prior elements.
|
||||
|
||||
```py path=short_circuit.py
|
||||
a = (1, 2)
|
||||
b = (999999, "hello")
|
||||
|
||||
reveal_type(a == b) # revealed: Literal[False]
|
||||
reveal_type(a != b) # revealed: Literal[True]
|
||||
reveal_type(a < b) # revealed: Literal[True]
|
||||
reveal_type(a <= b) # revealed: Literal[True]
|
||||
reveal_type(a > b) # revealed: Literal[False]
|
||||
reveal_type(a >= b) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
#### Matryoshka Tuples
|
||||
|
||||
```py
|
||||
a = (1, True, "Hello")
|
||||
b = (a, a, a)
|
||||
c = (b, b, b)
|
||||
|
||||
reveal_type(c == c) # revealed: Literal[True]
|
||||
reveal_type(c != c) # revealed: Literal[False]
|
||||
reveal_type(c < c) # revealed: Literal[False]
|
||||
reveal_type(c <= c) # revealed: Literal[True]
|
||||
reveal_type(c > c) # revealed: Literal[False]
|
||||
reveal_type(c >= c) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
#### Non Boolean Rich Comparisons
|
||||
|
||||
```py
|
||||
class A():
|
||||
def __eq__(self, o) -> str: ...
|
||||
def __ne__(self, o) -> int: ...
|
||||
def __lt__(self, o) -> float: ...
|
||||
def __le__(self, o) -> object: ...
|
||||
def __gt__(self, o) -> tuple: ...
|
||||
def __ge__(self, o) -> list: ...
|
||||
|
||||
a = (A(), A())
|
||||
|
||||
# TODO: All @Todo should be bool
|
||||
reveal_type(a == a) # revealed: @Todo
|
||||
reveal_type(a != a) # revealed: @Todo
|
||||
reveal_type(a < a) # revealed: @Todo
|
||||
reveal_type(a <= a) # revealed: @Todo
|
||||
reveal_type(a > a) # revealed: @Todo
|
||||
reveal_type(a >= a) # revealed: @Todo
|
||||
```
|
||||
|
||||
### Membership Test Comparisons
|
||||
|
||||
"Membership Test Comparisons" refers to the operators `in` and `not in`.
|
||||
|
||||
```py
|
||||
def int_instance() -> int: ...
|
||||
|
||||
a = (1, 2)
|
||||
b = ((3, 4), (1, 2))
|
||||
c = ((1, 2, 3), (4, 5, 6))
|
||||
d = ((int_instance(), int_instance()), (int_instance(), int_instance()))
|
||||
|
||||
reveal_type(a in b) # revealed: Literal[True]
|
||||
reveal_type(a not in b) # revealed: Literal[False]
|
||||
|
||||
reveal_type(a in c) # revealed: Literal[False]
|
||||
reveal_type(a not in c) # revealed: Literal[True]
|
||||
|
||||
# TODO: All @Todo should be bool
|
||||
reveal_type(a in d) # revealed: @Todo
|
||||
reveal_type(a not in d) # revealed: @Todo
|
||||
```
|
||||
|
||||
### Identity Comparisons
|
||||
|
||||
"Identity Comparisons" refers to `is` and `is not`.
|
||||
|
||||
```py
|
||||
a = (1, 2)
|
||||
b = ("a", "b")
|
||||
c = (1, 2, 3)
|
||||
|
||||
reveal_type(a is (1, 2)) # revealed: bool
|
||||
reveal_type(a is not (1, 2)) # revealed: bool
|
||||
|
||||
# TODO: Update to Literal[False] once str == int comparison is implemented
|
||||
reveal_type(a is b) # revealed: @Todo
|
||||
# TODO: Update to Literal[True] once str == int comparison is implemented
|
||||
reveal_type(a is not b) # revealed: @Todo
|
||||
|
||||
reveal_type(a is c) # revealed: Literal[False]
|
||||
reveal_type(a is not c) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
## Homogeneous
|
||||
|
||||
For tuples like `tuple[int, ...]`, `tuple[Any, ...]`
|
||||
|
||||
// TODO
|
||||
@@ -0,0 +1,76 @@
|
||||
# Comparison: Unions
|
||||
|
||||
## Union on one side of the comparison
|
||||
|
||||
Comparisons on union types need to consider all possible cases:
|
||||
|
||||
```py
|
||||
one_or_two = 1 if flag else 2
|
||||
|
||||
reveal_type(one_or_two <= 2) # revealed: Literal[True]
|
||||
reveal_type(one_or_two <= 1) # revealed: bool
|
||||
reveal_type(one_or_two <= 0) # revealed: Literal[False]
|
||||
|
||||
reveal_type(2 >= one_or_two) # revealed: Literal[True]
|
||||
reveal_type(1 >= one_or_two) # revealed: bool
|
||||
reveal_type(0 >= one_or_two) # revealed: Literal[False]
|
||||
|
||||
reveal_type(one_or_two < 1) # revealed: Literal[False]
|
||||
reveal_type(one_or_two < 2) # revealed: bool
|
||||
reveal_type(one_or_two < 3) # revealed: Literal[True]
|
||||
|
||||
reveal_type(one_or_two > 0) # revealed: Literal[True]
|
||||
reveal_type(one_or_two > 1) # revealed: bool
|
||||
reveal_type(one_or_two > 2) # revealed: Literal[False]
|
||||
|
||||
reveal_type(one_or_two == 3) # revealed: Literal[False]
|
||||
reveal_type(one_or_two == 1) # revealed: bool
|
||||
|
||||
reveal_type(one_or_two != 3) # revealed: Literal[True]
|
||||
reveal_type(one_or_two != 1) # revealed: bool
|
||||
|
||||
a_or_ab = "a" if flag else "ab"
|
||||
|
||||
reveal_type(a_or_ab in "ab") # revealed: Literal[True]
|
||||
reveal_type("a" in a_or_ab) # revealed: Literal[True]
|
||||
|
||||
reveal_type("c" not in a_or_ab) # revealed: Literal[True]
|
||||
reveal_type("a" not in a_or_ab) # revealed: Literal[False]
|
||||
|
||||
reveal_type("b" in a_or_ab) # revealed: bool
|
||||
reveal_type("b" not in a_or_ab) # revealed: bool
|
||||
|
||||
one_or_none = 1 if flag else None
|
||||
|
||||
reveal_type(one_or_none is None) # revealed: bool
|
||||
reveal_type(one_or_none is not None) # revealed: bool
|
||||
```
|
||||
|
||||
## Union on both sides of the comparison
|
||||
|
||||
With unions on both sides, we need to consider the full cross product of
|
||||
options when building the resulting (union) type:
|
||||
|
||||
```py
|
||||
small = 1 if flag_s else 2
|
||||
large = 2 if flag_l else 3
|
||||
|
||||
reveal_type(small <= large) # revealed: Literal[True]
|
||||
reveal_type(small >= large) # revealed: bool
|
||||
|
||||
reveal_type(small < large) # revealed: bool
|
||||
reveal_type(small > large) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Unsupported operations
|
||||
|
||||
Make sure we emit a diagnostic if *any* of the possible comparisons is
|
||||
unsupported. For now, we fall back to `bool` for the result type instead of
|
||||
trying to infer something more precise from the other (supported) variants:
|
||||
|
||||
```py
|
||||
x = [1, 2] if flag else 1
|
||||
|
||||
result = 1 in x # error: "Operator `in` is not supported"
|
||||
reveal_type(result) # revealed: bool
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# Unsupported operators
|
||||
|
||||
```py
|
||||
a = 1 in 7 # error: "Operator `in` is not supported for types `Literal[1]` and `Literal[7]`"
|
||||
b = 0 not in 10 # error: "Operator `not in` is not supported for types `Literal[0]` and `Literal[10]`"
|
||||
c = object() < 5 # error: "Operator `<` is not supported for types `object` and `Literal[5]`"
|
||||
# TODO should error, need to check if __lt__ signature is valid for right operand
|
||||
d = 5 < object()
|
||||
|
||||
reveal_type(a) # revealed: bool
|
||||
reveal_type(b) # revealed: bool
|
||||
reveal_type(c) # revealed: Unknown
|
||||
# TODO: should be `Unknown`
|
||||
reveal_type(d) # revealed: bool
|
||||
```
|
||||
@@ -0,0 +1,35 @@
|
||||
# If expressions
|
||||
|
||||
## Simple if-expression
|
||||
|
||||
```py
|
||||
x = 1 if flag else 2
|
||||
reveal_type(x) # revealed: Literal[1, 2]
|
||||
```
|
||||
|
||||
## If-expression with walrus operator
|
||||
|
||||
```py
|
||||
y = 0
|
||||
z = 0
|
||||
x = (y := 1) if flag else (z := 2)
|
||||
a = y
|
||||
b = z
|
||||
reveal_type(x) # revealed: Literal[1, 2]
|
||||
reveal_type(a) # revealed: Literal[0, 1]
|
||||
reveal_type(b) # revealed: Literal[0, 2]
|
||||
```
|
||||
|
||||
## Nested if-expression
|
||||
|
||||
```py
|
||||
x = 1 if flag else 2 if flag2 else 3
|
||||
reveal_type(x) # revealed: Literal[1, 2, 3]
|
||||
```
|
||||
|
||||
## None
|
||||
|
||||
```py
|
||||
x = 1 if flag else None
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
```
|
||||
@@ -0,0 +1,98 @@
|
||||
# If statements
|
||||
|
||||
## Simple if
|
||||
|
||||
```py
|
||||
y = 1
|
||||
y = 2
|
||||
|
||||
if flag:
|
||||
y = 3
|
||||
|
||||
x = y
|
||||
|
||||
reveal_type(x) # revealed: Literal[2, 3]
|
||||
```
|
||||
|
||||
## Simple if-elif-else
|
||||
|
||||
```py
|
||||
y = 1
|
||||
y = 2
|
||||
if flag:
|
||||
y = 3
|
||||
elif flag2:
|
||||
y = 4
|
||||
else:
|
||||
r = y
|
||||
y = 5
|
||||
s = y
|
||||
x = y
|
||||
|
||||
reveal_type(x) # revealed: Literal[3, 4, 5]
|
||||
reveal_type(r) # revealed: Unbound | Literal[2]
|
||||
reveal_type(s) # revealed: Unbound | Literal[5]
|
||||
```
|
||||
|
||||
## Single symbol across if-elif-else
|
||||
|
||||
```py
|
||||
if flag:
|
||||
y = 1
|
||||
elif flag2:
|
||||
y = 2
|
||||
else:
|
||||
y = 3
|
||||
reveal_type(y) # revealed: Literal[1, 2, 3]
|
||||
```
|
||||
|
||||
## if-elif-else without else assignment
|
||||
|
||||
```py
|
||||
y = 0
|
||||
if flag:
|
||||
y = 1
|
||||
elif flag2:
|
||||
y = 2
|
||||
else:
|
||||
pass
|
||||
reveal_type(y) # revealed: Literal[0, 1, 2]
|
||||
```
|
||||
|
||||
## if-elif-else with intervening assignment
|
||||
|
||||
```py
|
||||
y = 0
|
||||
if flag:
|
||||
y = 1
|
||||
z = 3
|
||||
elif flag2:
|
||||
y = 2
|
||||
else:
|
||||
pass
|
||||
reveal_type(y) # revealed: Literal[0, 1, 2]
|
||||
```
|
||||
|
||||
## Nested if statement
|
||||
|
||||
```py
|
||||
y = 0
|
||||
if flag:
|
||||
if flag2:
|
||||
y = 1
|
||||
reveal_type(y) # revealed: Literal[0, 1]
|
||||
```
|
||||
|
||||
## if-elif without else
|
||||
|
||||
```py
|
||||
y = 1
|
||||
y = 2
|
||||
if flag:
|
||||
y = 3
|
||||
elif flag2:
|
||||
y = 4
|
||||
x = y
|
||||
|
||||
reveal_type(x) # revealed: Literal[2, 3, 4]
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
# Pattern matching
|
||||
|
||||
## With wildcard
|
||||
|
||||
```py
|
||||
match 0:
|
||||
case 1:
|
||||
y = 2
|
||||
case _:
|
||||
y = 3
|
||||
|
||||
reveal_type(y) # revealed: Literal[2, 3]
|
||||
```
|
||||
|
||||
## Without wildcard
|
||||
|
||||
```py
|
||||
match 0:
|
||||
case 1:
|
||||
y = 2
|
||||
case 2:
|
||||
y = 3
|
||||
|
||||
reveal_type(y) # revealed: Unbound | Literal[2, 3]
|
||||
```
|
||||
|
||||
## Basic match
|
||||
|
||||
```py
|
||||
y = 1
|
||||
y = 2
|
||||
match 0:
|
||||
case 1:
|
||||
y = 3
|
||||
case 2:
|
||||
y = 4
|
||||
|
||||
reveal_type(y) # revealed: Literal[2, 3, 4]
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
# Errors while declaring
|
||||
|
||||
## Violates previous assignment
|
||||
|
||||
```py
|
||||
x = 1
|
||||
x: str # error: [invalid-declaration] "Cannot declare type `str` for inferred type `Literal[1]`"
|
||||
```
|
||||
|
||||
## Incompatible declarations
|
||||
|
||||
```py
|
||||
if flag:
|
||||
x: str
|
||||
else:
|
||||
x: int
|
||||
x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: str, int"
|
||||
```
|
||||
|
||||
## Partial declarations
|
||||
|
||||
```py
|
||||
if flag:
|
||||
x: int
|
||||
x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: Unknown, int"
|
||||
```
|
||||
|
||||
## Incompatible declarations with bad assignment
|
||||
|
||||
```py
|
||||
if flag:
|
||||
x: str
|
||||
else:
|
||||
x: int
|
||||
|
||||
# error: [conflicting-declarations]
|
||||
# error: [invalid-assignment]
|
||||
x = b'foo'
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
# Exception Handling
|
||||
|
||||
## Single Exception
|
||||
|
||||
```py
|
||||
import re
|
||||
try:
|
||||
x
|
||||
except NameError as e:
|
||||
reveal_type(e) # revealed: NameError
|
||||
except re.error as f:
|
||||
reveal_type(f) # revealed: error
|
||||
```
|
||||
|
||||
## Unknown type in except handler does not cause spurious diagnostic
|
||||
|
||||
```py
|
||||
from nonexistent_module import foo # error: [unresolved-import]
|
||||
|
||||
try:
|
||||
x
|
||||
except foo as e:
|
||||
reveal_type(foo) # revealed: Unknown
|
||||
reveal_type(e) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Multiple Exceptions in a Tuple
|
||||
|
||||
```py
|
||||
EXCEPTIONS = (AttributeError, TypeError)
|
||||
|
||||
try:
|
||||
x
|
||||
except (RuntimeError, OSError) as e:
|
||||
reveal_type(e) # revealed: RuntimeError | OSError
|
||||
except EXCEPTIONS as f:
|
||||
reveal_type(f) # revealed: AttributeError | TypeError
|
||||
```
|
||||
|
||||
## Dynamic exception types
|
||||
|
||||
```py
|
||||
def foo(x: type[AttributeError], y: tuple[type[OSError], type[RuntimeError]], z: tuple[type[BaseException], ...]):
|
||||
try:
|
||||
w
|
||||
except x as e:
|
||||
# TODO: should be `AttributeError`
|
||||
reveal_type(e) # revealed: @Todo
|
||||
except y as f:
|
||||
# TODO: should be `OSError | RuntimeError`
|
||||
reveal_type(f) # revealed: @Todo
|
||||
except z as g:
|
||||
# TODO: should be `BaseException`
|
||||
reveal_type(g) # revealed: @Todo
|
||||
```
|
||||
@@ -0,0 +1,641 @@
|
||||
# Control flow for exception handlers
|
||||
|
||||
These tests assert that we understand the possible "definition states" (which
|
||||
symbols might or might not be defined) in the various branches of a
|
||||
`try`/`except`/`else`/`finally` block.
|
||||
|
||||
For a full writeup on the semantics of exception handlers,
|
||||
see [this document][1].
|
||||
|
||||
The tests throughout this Markdown document use functions with names starting
|
||||
with `could_raise_*` to mark definitions that might or might not succeed
|
||||
(as the function could raise an exception). A type checker must assume that any
|
||||
arbitrary function call could raise an exception in Python; this is just a
|
||||
naming convention used in these tests for clarity, and to future-proof the
|
||||
tests against possible future improvements whereby certain statements or
|
||||
expressions could potentially be inferred as being incapable of causing an
|
||||
exception to be raised.
|
||||
|
||||
## A single bare `except`
|
||||
|
||||
Consider the following `try`/`except` block, with a single bare `except:`.
|
||||
There are different types for the variable `x` in the two branches of this
|
||||
block, and we can't determine which branch might have been taken from the
|
||||
perspective of code following this block. The inferred type after the block's
|
||||
conclusion is therefore the union of the type at the end of the `try` suite
|
||||
(`str`) and the type at the end of the `except` suite (`Literal[2]`).
|
||||
|
||||
*Within* the `except` suite, we must infer a union of all possible "definition
|
||||
states" we could have been in at any point during the `try` suite. This is
|
||||
because control flow could have jumped to the `except` suite without any of the
|
||||
`try`-suite definitions successfully completing, with only *some* of the
|
||||
`try`-suite definitions successfully completing, or indeed with *all* of them
|
||||
successfully completing. The type of `x` at the beginning of the `except` suite
|
||||
in this example is therefore `Literal[1] | str`, taking into account that we
|
||||
might have jumped to the `except` suite before the
|
||||
`x = could_raise_returns_str()` redefinition, but we *also* could have jumped
|
||||
to the `except` suite *after* that redefinition.
|
||||
|
||||
```py path=union_type_inferred.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
|
||||
reveal_type(x) # revealed: str | Literal[2]
|
||||
```
|
||||
|
||||
If `x` has the same type at the end of both branches, however, the branches
|
||||
unify and `x` is not inferred as having a union type following the
|
||||
`try`/`except` block:
|
||||
|
||||
```py path=branches_unify_to_non_union_type.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
x = could_raise_returns_str()
|
||||
except:
|
||||
x = could_raise_returns_str()
|
||||
|
||||
reveal_type(x) # revealed: str
|
||||
```
|
||||
|
||||
## A non-bare `except`
|
||||
|
||||
For simple `try`/`except` blocks, an `except TypeError:` handler has the same
|
||||
control flow semantics as an `except:` handler. An `except TypeError:` handler
|
||||
will not catch *all* exceptions: if this is the only handler, it opens up the
|
||||
possibility that an exception might occur that would not be handled. However,
|
||||
as described in [the document on exception-handling semantics][1], that would
|
||||
lead to termination of the scope. It's therefore irrelevant to consider this
|
||||
possibility when it comes to control-flow analysis.
|
||||
|
||||
```py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
|
||||
reveal_type(x) # revealed: str | Literal[2]
|
||||
```
|
||||
|
||||
## Multiple `except` branches
|
||||
|
||||
If the scope reaches the final `reveal_type` call in this example,
|
||||
either the `try`-block suite of statements was executed in its entirety,
|
||||
or exactly one `except` suite was executed in its entirety.
|
||||
The inferred type of `x` at this point is the union of the types at the end of
|
||||
the three suites:
|
||||
|
||||
- At the end of `try`, `type(x) == str`
|
||||
- At the end of `except TypeError`, `x == 2`
|
||||
- At the end of `except ValueError`, `x == 3`
|
||||
|
||||
```py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
except ValueError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = 3
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
|
||||
reveal_type(x) # revealed: str | Literal[2, 3]
|
||||
```
|
||||
|
||||
## Exception handlers with `else` branches (but no `finally`)
|
||||
|
||||
If we reach the `reveal_type` call at the end of this scope,
|
||||
either the `try` and `else` suites were both executed in their entireties,
|
||||
or the `except` suite was executed in its entirety. The type of `x` at this
|
||||
point is the union of the type at the end of the `else` suite and the type at
|
||||
the end of the `except` suite:
|
||||
|
||||
- At the end of `else`, `x == 3`
|
||||
- At the end of `except`, `x == 2`
|
||||
|
||||
```py path=single_except.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
else:
|
||||
reveal_type(x) # revealed: str
|
||||
x = 3
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
|
||||
reveal_type(x) # revealed: Literal[2, 3]
|
||||
```
|
||||
|
||||
For a block that has multiple `except` branches and an `else` branch, the same
|
||||
principle applies. In order to reach the final `reveal_type` call,
|
||||
either exactly one of the `except` suites must have been executed in its
|
||||
entirety, or the `try` suite and the `else` suite must both have been executed
|
||||
in their entireties:
|
||||
|
||||
```py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
except ValueError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = 3
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
else:
|
||||
reveal_type(x) # revealed: str
|
||||
x = 4
|
||||
reveal_type(x) # revealed: Literal[4]
|
||||
|
||||
reveal_type(x) # revealed: Literal[2, 3, 4]
|
||||
```
|
||||
|
||||
## Exception handlers with `finally` branches (but no `except` branches)
|
||||
|
||||
A `finally` suite is *always* executed. As such, if we reach the `reveal_type`
|
||||
call at the end of this example, we know that `x` *must* have been reassigned
|
||||
to `2` during the `finally` suite. The type of `x` at the end of the example is
|
||||
therefore `Literal[2]`:
|
||||
|
||||
```py path=redef_in_finally.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
finally:
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
If `x` was *not* redefined in the `finally` suite, however, things are somewhat
|
||||
more complicated. If we reach the final `reveal_type` call,
|
||||
unlike the state when we're visiting the `finally` suite,
|
||||
we know that the `try`-block suite ran to completion.
|
||||
This means that there are fewer possible states at this point than there were
|
||||
when we were inside the `finally` block.
|
||||
|
||||
(Our current model does *not* correctly infer the types *inside* `finally`
|
||||
suites, however; this is still a TODO item for us.)
|
||||
|
||||
```py path=no_redef_in_finally.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
finally:
|
||||
# TODO: should be Literal[1] | str
|
||||
reveal_type(x) # revealed: str
|
||||
|
||||
reveal_type(x) # revealed: str
|
||||
```
|
||||
|
||||
## Combining an `except` branch with a `finally` branch
|
||||
|
||||
As previously stated, we do not yet have accurate inference for types *inside*
|
||||
`finally` suites. When we do, however, we will have to take account of the
|
||||
following possibilities inside `finally` suites:
|
||||
|
||||
- The `try` suite could have run to completion
|
||||
- Or we could have jumped from halfway through the `try` suite to an `except`
|
||||
suite, and the `except` suite ran to completion
|
||||
- Or we could have jumped from halfway through the `try` suite straight to the
|
||||
`finally` suite due to an unhandled exception
|
||||
- Or we could have jumped from halfway through the `try` suite to an
|
||||
`except` suite, only for an exception raised in the `except` suite to cause
|
||||
us to jump to the `finally` suite before the `except` suite ran to completion
|
||||
|
||||
```py path=redef_in_finally.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
def could_raise_returns_bytes() -> bytes:
|
||||
return b'foo'
|
||||
|
||||
def could_raise_returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_bytes()
|
||||
reveal_type(x) # revealed: bytes
|
||||
x = could_raise_returns_bool()
|
||||
reveal_type(x) # revealed: bool
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | str | bytes | bool`
|
||||
reveal_type(x) # revealed: str | bool
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
Now for an example without a redefinition in the `finally` suite.
|
||||
As before, there *should* be fewer possibilities after completion of the
|
||||
`finally` suite than there were during the `finally` suite itself.
|
||||
(In some control-flow possibilities, some exceptions were merely *suspended*
|
||||
during the `finally` suite; these lead to the scope's termination following the
|
||||
conclusion of the `finally` suite.)
|
||||
|
||||
```py path=no_redef_in_finally.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
def could_raise_returns_bytes() -> bytes:
|
||||
return b'foo'
|
||||
|
||||
def could_raise_returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_bytes()
|
||||
reveal_type(x) # revealed: bytes
|
||||
x = could_raise_returns_bool()
|
||||
reveal_type(x) # revealed: bool
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | str | bytes | bool`
|
||||
reveal_type(x) # revealed: str | bool
|
||||
|
||||
reveal_type(x) # revealed: str | bool
|
||||
```
|
||||
|
||||
An example with multiple `except` branches and a `finally` branch:
|
||||
|
||||
```py path=multiple_except_branches.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
def could_raise_returns_bytes() -> bytes:
|
||||
return b'foo'
|
||||
|
||||
def could_raise_returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
def could_raise_returns_memoryview() -> memoryview:
|
||||
return memoryview(b"")
|
||||
|
||||
def could_raise_returns_float() -> float:
|
||||
return 3.14
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_bytes()
|
||||
reveal_type(x) # revealed: bytes
|
||||
x = could_raise_returns_bool()
|
||||
reveal_type(x) # revealed: bool
|
||||
except ValueError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_memoryview()
|
||||
reveal_type(x) # revealed: memoryview
|
||||
x = could_raise_returns_float()
|
||||
reveal_type(x) # revealed: float
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | str | bytes | bool | memoryview | float`
|
||||
reveal_type(x) # revealed: str | bool | float
|
||||
|
||||
reveal_type(x) # revealed: str | bool | float
|
||||
```
|
||||
|
||||
## Combining `except`, `else` and `finally` branches
|
||||
|
||||
If the exception handler has an `else` branch, we must also take into account
|
||||
the possibility that control flow could have jumped to the `finally` suite from
|
||||
partway through the `else` suite due to an exception raised *there*.
|
||||
|
||||
```py path=single_except_branch.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
def could_raise_returns_bytes() -> bytes:
|
||||
return b'foo'
|
||||
|
||||
def could_raise_returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
def could_raise_returns_memoryview() -> memoryview:
|
||||
return memoryview(b"")
|
||||
|
||||
def could_raise_returns_float() -> float:
|
||||
return 3.14
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_bytes()
|
||||
reveal_type(x) # revealed: bytes
|
||||
x = could_raise_returns_bool()
|
||||
reveal_type(x) # revealed: bool
|
||||
else:
|
||||
reveal_type(x) # revealed: str
|
||||
x = could_raise_returns_memoryview()
|
||||
reveal_type(x) # revealed: memoryview
|
||||
x = could_raise_returns_float()
|
||||
reveal_type(x) # revealed: float
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | str | bytes | bool | memoryview | float`
|
||||
reveal_type(x) # revealed: bool | float
|
||||
|
||||
reveal_type(x) # revealed: bool | float
|
||||
```
|
||||
|
||||
The same again, this time with multiple `except` branches:
|
||||
|
||||
```py path=multiple_except_branches.py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
def could_raise_returns_bytes() -> bytes:
|
||||
return b'foo'
|
||||
|
||||
def could_raise_returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
def could_raise_returns_memoryview() -> memoryview:
|
||||
return memoryview(b"")
|
||||
|
||||
def could_raise_returns_float() -> float:
|
||||
return 3.14
|
||||
|
||||
def could_raise_returns_range() -> range:
|
||||
return range(42)
|
||||
|
||||
def could_raise_returns_slice() -> slice:
|
||||
return slice(None)
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_bytes()
|
||||
reveal_type(x) # revealed: bytes
|
||||
x = could_raise_returns_bool()
|
||||
reveal_type(x) # revealed: bool
|
||||
except ValueError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_memoryview()
|
||||
reveal_type(x) # revealed: memoryview
|
||||
x = could_raise_returns_float()
|
||||
reveal_type(x) # revealed: float
|
||||
else:
|
||||
reveal_type(x) # revealed: str
|
||||
x = could_raise_returns_range()
|
||||
reveal_type(x) # revealed: range
|
||||
x = could_raise_returns_slice()
|
||||
reveal_type(x) # revealed: slice
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | str | bytes | bool | memoryview | float | range | slice`
|
||||
reveal_type(x) # revealed: bool | float | slice
|
||||
|
||||
reveal_type(x) # revealed: bool | float | slice
|
||||
```
|
||||
|
||||
## Nested `try`/`except` blocks
|
||||
|
||||
It would take advanced analysis, which we are not yet capable of, to be able
|
||||
to determine that an exception handler always suppresses all exceptions. This
|
||||
is partly because it is possible for statements in `except`, `else` and
|
||||
`finally` suites to raise exceptions as well as statements in `try` suites.
|
||||
This means that if an exception handler is nested inside the `try` statement of
|
||||
an enclosing exception handler, it should (at least for now) be treated the
|
||||
same as any other node: as a suite containing statements that could possibly
|
||||
raise exceptions, which would lead to control flow jumping out of that suite
|
||||
prior to the suite running to completion.
|
||||
|
||||
```py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
def could_raise_returns_bytes() -> bytes:
|
||||
return b'foo'
|
||||
|
||||
def could_raise_returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
def could_raise_returns_memoryview() -> memoryview:
|
||||
return memoryview(b"")
|
||||
|
||||
def could_raise_returns_float() -> float:
|
||||
return 3.14
|
||||
|
||||
def could_raise_returns_range() -> range:
|
||||
return range(42)
|
||||
|
||||
def could_raise_returns_slice() -> slice:
|
||||
return slice(None)
|
||||
|
||||
def could_raise_returns_complex() -> complex:
|
||||
return 3j
|
||||
|
||||
def could_raise_returns_bytearray() -> bytearray:
|
||||
return bytearray()
|
||||
|
||||
class Foo: ...
|
||||
class Bar: ...
|
||||
|
||||
def could_raise_returns_Foo() -> Foo:
|
||||
return Foo()
|
||||
|
||||
def could_raise_returns_Bar() -> Bar:
|
||||
return Bar()
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
try:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
x = could_raise_returns_str()
|
||||
reveal_type(x) # revealed: str
|
||||
except TypeError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_bytes()
|
||||
reveal_type(x) # revealed: bytes
|
||||
x = could_raise_returns_bool()
|
||||
reveal_type(x) # revealed: bool
|
||||
except ValueError:
|
||||
reveal_type(x) # revealed: Literal[1] | str
|
||||
x = could_raise_returns_memoryview()
|
||||
reveal_type(x) # revealed: memoryview
|
||||
x = could_raise_returns_float()
|
||||
reveal_type(x) # revealed: float
|
||||
else:
|
||||
reveal_type(x) # revealed: str
|
||||
x = could_raise_returns_range()
|
||||
reveal_type(x) # revealed: range
|
||||
x = could_raise_returns_slice()
|
||||
reveal_type(x) # revealed: slice
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | str | bytes | bool | memoryview | float | range | slice`
|
||||
reveal_type(x) # revealed: bool | float | slice
|
||||
x = 2
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
except:
|
||||
reveal_type(x) # revealed: Literal[1, 2] | str | bytes | bool | memoryview | float | range | slice
|
||||
x = could_raise_returns_complex()
|
||||
reveal_type(x) # revealed: complex
|
||||
x = could_raise_returns_bytearray()
|
||||
reveal_type(x) # revealed: bytearray
|
||||
else:
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
x = could_raise_returns_Foo()
|
||||
reveal_type(x) # revealed: Foo
|
||||
x = could_raise_returns_Bar()
|
||||
reveal_type(x) # revealed: Bar
|
||||
finally:
|
||||
# TODO: should be `Literal[1, 2] | str | bytes | bool | memoryview | float | range | slice | complex | bytearray | Foo | Bar`
|
||||
reveal_type(x) # revealed: bytearray | Bar
|
||||
|
||||
# Either one `except` branch or the `else`
|
||||
# must have been taken and completed to get here:
|
||||
reveal_type(x) # revealed: bytearray | Bar
|
||||
```
|
||||
|
||||
## Nested scopes inside `try` blocks
|
||||
|
||||
Shadowing a variable in an inner scope has no effect on type inference of the
|
||||
variable by that name in the outer scope:
|
||||
|
||||
```py
|
||||
def could_raise_returns_str() -> str:
|
||||
return 'foo'
|
||||
|
||||
def could_raise_returns_bytes() -> bytes:
|
||||
return b'foo'
|
||||
|
||||
def could_raise_returns_range() -> range:
|
||||
return range(42)
|
||||
|
||||
def could_raise_returns_bytearray() -> bytearray:
|
||||
return bytearray()
|
||||
|
||||
def could_raise_returns_float() -> float:
|
||||
return 3.14
|
||||
|
||||
x = 1
|
||||
|
||||
try:
|
||||
def foo(param=could_raise_returns_str()):
|
||||
x = could_raise_returns_str()
|
||||
|
||||
try:
|
||||
reveal_type(x) # revealed: str
|
||||
x = could_raise_returns_bytes()
|
||||
reveal_type(x) # revealed: bytes
|
||||
except:
|
||||
reveal_type(x) # revealed: str | bytes
|
||||
x = could_raise_returns_bytearray()
|
||||
reveal_type(x) # revealed: bytearray
|
||||
x = could_raise_returns_float()
|
||||
reveal_type(x) # revealed: float
|
||||
finally:
|
||||
# TODO: should be `str | bytes | bytearray | float`
|
||||
reveal_type(x) # revealed: bytes | float
|
||||
reveal_type(x) # revealed: bytes | float
|
||||
|
||||
x = foo
|
||||
reveal_type(x) # revealed: Literal[foo]
|
||||
except:
|
||||
reveal_type(x) # revealed: Literal[1] | Literal[foo]
|
||||
|
||||
class Bar:
|
||||
x = could_raise_returns_range()
|
||||
reveal_type(x) # revealed: range
|
||||
|
||||
x = Bar
|
||||
reveal_type(x) # revealed: Literal[Bar]
|
||||
finally:
|
||||
# TODO: should be `Literal[1] | Literal[foo] | Literal[Bar]`
|
||||
reveal_type(x) # revealed: Literal[foo] | Literal[Bar]
|
||||
|
||||
reveal_type(x) # revealed: Literal[foo] | Literal[Bar]
|
||||
```
|
||||
|
||||
[1]: https://astral-sh.notion.site/Exception-handler-control-flow-11348797e1ca80bb8ce1e9aedbbe439d
|
||||
@@ -0,0 +1,30 @@
|
||||
# Except star
|
||||
|
||||
## Except\* with BaseException
|
||||
|
||||
```py
|
||||
try:
|
||||
x
|
||||
except* BaseException as e:
|
||||
reveal_type(e) # revealed: BaseExceptionGroup
|
||||
```
|
||||
|
||||
## Except\* with specific exception
|
||||
|
||||
```py
|
||||
try:
|
||||
x
|
||||
except* OSError as e:
|
||||
# TODO(Alex): more precise would be `ExceptionGroup[OSError]`
|
||||
reveal_type(e) # revealed: BaseExceptionGroup
|
||||
```
|
||||
|
||||
## Except\* with multiple exceptions
|
||||
|
||||
```py
|
||||
try:
|
||||
x
|
||||
except* (TypeError, AttributeError) as e:
|
||||
#TODO(Alex): more precise would be `ExceptionGroup[TypeError | AttributeError]`.
|
||||
reveal_type(e) # revealed: BaseExceptionGroup
|
||||
```
|
||||
@@ -0,0 +1,151 @@
|
||||
# Expressions
|
||||
|
||||
## OR
|
||||
|
||||
```py
|
||||
def foo() -> str:
|
||||
pass
|
||||
|
||||
a = True or False
|
||||
b = 'x' or 'y' or 'z'
|
||||
c = '' or 'y' or 'z'
|
||||
d = False or 'z'
|
||||
e = False or True
|
||||
f = False or False
|
||||
g = foo() or False
|
||||
h = foo() or True
|
||||
|
||||
reveal_type(a) # revealed: Literal[True]
|
||||
reveal_type(b) # revealed: Literal["x"]
|
||||
reveal_type(c) # revealed: Literal["y"]
|
||||
reveal_type(d) # revealed: Literal["z"]
|
||||
reveal_type(e) # revealed: Literal[True]
|
||||
reveal_type(f) # revealed: Literal[False]
|
||||
reveal_type(g) # revealed: str | Literal[False]
|
||||
reveal_type(h) # revealed: str | Literal[True]
|
||||
```
|
||||
|
||||
## AND
|
||||
|
||||
```py
|
||||
def foo() -> str:
|
||||
pass
|
||||
|
||||
a = True and False
|
||||
b = False and True
|
||||
c = foo() and False
|
||||
d = foo() and True
|
||||
e = 'x' and 'y' and 'z'
|
||||
f = 'x' and 'y' and ''
|
||||
g = '' and 'y'
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: Literal[False]
|
||||
reveal_type(c) # revealed: str | Literal[False]
|
||||
reveal_type(d) # revealed: str | Literal[True]
|
||||
reveal_type(e) # revealed: Literal["z"]
|
||||
reveal_type(f) # revealed: Literal[""]
|
||||
reveal_type(g) # revealed: Literal[""]
|
||||
```
|
||||
|
||||
## Simple function calls to bool
|
||||
|
||||
```py
|
||||
def returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
if returns_bool():
|
||||
x = True
|
||||
else:
|
||||
x = False
|
||||
|
||||
reveal_type(x) # revealed: bool
|
||||
```
|
||||
|
||||
## Complex
|
||||
|
||||
```py
|
||||
def foo() -> str:
|
||||
pass
|
||||
|
||||
a = "x" and "y" or "z"
|
||||
b = "x" or "y" and "z"
|
||||
c = "" and "y" or "z"
|
||||
d = "" or "y" and "z"
|
||||
e = "x" and "y" or ""
|
||||
f = "x" or "y" and ""
|
||||
|
||||
reveal_type(a) # revealed: Literal["y"]
|
||||
reveal_type(b) # revealed: Literal["x"]
|
||||
reveal_type(c) # revealed: Literal["z"]
|
||||
reveal_type(d) # revealed: Literal["z"]
|
||||
reveal_type(e) # revealed: Literal["y"]
|
||||
reveal_type(f) # revealed: Literal["x"]
|
||||
```
|
||||
|
||||
## `bool()` function
|
||||
|
||||
## Evaluates to builtin
|
||||
|
||||
```py path=a.py
|
||||
redefined_builtin_bool = bool
|
||||
|
||||
def my_bool(x)-> bool: pass
|
||||
```
|
||||
|
||||
```py
|
||||
from a import redefined_builtin_bool, my_bool
|
||||
a = redefined_builtin_bool(0)
|
||||
b = my_bool(0)
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: bool
|
||||
```
|
||||
|
||||
## Truthy values
|
||||
|
||||
```py
|
||||
a = bool(1)
|
||||
b = bool((0,))
|
||||
c = bool("NON EMPTY")
|
||||
d = bool(True)
|
||||
|
||||
def foo(): pass
|
||||
e = bool(foo)
|
||||
|
||||
reveal_type(a) # revealed: Literal[True]
|
||||
reveal_type(b) # revealed: Literal[True]
|
||||
reveal_type(c) # revealed: Literal[True]
|
||||
reveal_type(d) # revealed: Literal[True]
|
||||
reveal_type(e) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
## Falsy values
|
||||
|
||||
```py
|
||||
a = bool(0)
|
||||
b = bool(())
|
||||
c = bool(None)
|
||||
d = bool("")
|
||||
e = bool(False)
|
||||
f = bool()
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: Literal[False]
|
||||
reveal_type(c) # revealed: Literal[False]
|
||||
reveal_type(d) # revealed: Literal[False]
|
||||
reveal_type(e) # revealed: Literal[False]
|
||||
reveal_type(f) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Ambiguous values
|
||||
|
||||
```py
|
||||
a = bool([])
|
||||
b = bool({})
|
||||
c = bool(set())
|
||||
|
||||
reveal_type(a) # revealed: bool
|
||||
reveal_type(b) # revealed: bool
|
||||
reveal_type(c) # revealed: bool
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
# Structures
|
||||
|
||||
## Class import following
|
||||
|
||||
```py
|
||||
from b import C as D; E = D
|
||||
reveal_type(E) # revealed: Literal[C]
|
||||
```
|
||||
|
||||
```py path=b.py
|
||||
class C: pass
|
||||
```
|
||||
|
||||
## Module member resolution
|
||||
|
||||
```py
|
||||
import b; D = b.C
|
||||
reveal_type(D) # revealed: Literal[C]
|
||||
```
|
||||
|
||||
```py path=b.py
|
||||
class C: pass
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
# Importing builtin module
|
||||
|
||||
```py
|
||||
import builtins; x = builtins.copyright
|
||||
reveal_type(x) # revealed: Literal[copyright]
|
||||
```
|
||||
@@ -0,0 +1,75 @@
|
||||
# Conditional imports
|
||||
|
||||
## Maybe unbound
|
||||
|
||||
```py path=maybe_unbound.py
|
||||
if flag:
|
||||
y = 3
|
||||
x = y
|
||||
reveal_type(x) # revealed: Unbound | Literal[3]
|
||||
reveal_type(y) # revealed: Unbound | Literal[3]
|
||||
```
|
||||
|
||||
```py
|
||||
from maybe_unbound import x, y
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
reveal_type(y) # revealed: Literal[3]
|
||||
```
|
||||
|
||||
## Maybe unbound annotated
|
||||
|
||||
```py path=maybe_unbound_annotated.py
|
||||
if flag:
|
||||
y: int = 3
|
||||
x = y
|
||||
reveal_type(x) # revealed: Unbound | Literal[3]
|
||||
reveal_type(y) # revealed: Unbound | Literal[3]
|
||||
```
|
||||
|
||||
Importing an annotated name prefers the declared type over the inferred type:
|
||||
|
||||
```py
|
||||
from maybe_unbound_annotated import x, y
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
reveal_type(y) # revealed: int
|
||||
```
|
||||
|
||||
## Reimport
|
||||
|
||||
```py path=c.py
|
||||
def f(): ...
|
||||
```
|
||||
|
||||
```py path=b.py
|
||||
if flag:
|
||||
from c import f
|
||||
else:
|
||||
def f(): ...
|
||||
```
|
||||
|
||||
```py
|
||||
from b import f
|
||||
# TODO: We should disambiguate in such cases, showing `Literal[b.f, c.f]`.
|
||||
reveal_type(f) # revealed: Literal[f, f]
|
||||
```
|
||||
|
||||
## Reimport with stub declaration
|
||||
|
||||
When we have a declared type in one path and only an inferred-from-definition type in the other, we
|
||||
should still be able to unify those:
|
||||
|
||||
```py path=c.pyi
|
||||
x: int
|
||||
```
|
||||
|
||||
```py path=b.py
|
||||
if flag:
|
||||
from c import x
|
||||
else:
|
||||
x = 1
|
||||
```
|
||||
|
||||
```py
|
||||
from b import x
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
# Unresolved Imports
|
||||
|
||||
## Unresolved import statement
|
||||
|
||||
```py
|
||||
import bar # error: "Cannot resolve import `bar`"
|
||||
reveal_type(bar) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Unresolved import from statement
|
||||
|
||||
```py
|
||||
from bar import baz # error: "Cannot resolve import `bar`"
|
||||
reveal_type(baz) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Unresolved import from resolved module
|
||||
|
||||
```py path=a.py
|
||||
```
|
||||
|
||||
```py
|
||||
from a import thing # error: "Module `a` has no member `thing`"
|
||||
reveal_type(thing) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Resolved import of symbol from unresolved import
|
||||
|
||||
```py path=a.py
|
||||
import foo as foo # error: "Cannot resolve import `foo`"
|
||||
reveal_type(foo) # revealed: Unknown
|
||||
```
|
||||
|
||||
Importing the unresolved import into a second file should not trigger an additional "unresolved
|
||||
import" violation:
|
||||
|
||||
```py
|
||||
from a import foo
|
||||
reveal_type(foo) # revealed: Unknown
|
||||
```
|
||||
|
||||
## No implicit shadowing
|
||||
|
||||
```py path=b.py
|
||||
x: int
|
||||
```
|
||||
|
||||
```py
|
||||
from b import x
|
||||
|
||||
x = 'foo' # error: [invalid-assignment] "Object of type `Literal["foo"]"
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
# Relative
|
||||
|
||||
## Non-existent
|
||||
|
||||
```py path=package/__init__.py
|
||||
```
|
||||
|
||||
```py path=package/bar.py
|
||||
from .foo import X # error: [unresolved-import]
|
||||
reveal_type(X) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Simple
|
||||
|
||||
```py path=package/__init__.py
|
||||
```
|
||||
|
||||
```py path=package/foo.py
|
||||
X = 42
|
||||
```
|
||||
|
||||
```py path=package/bar.py
|
||||
from .foo import X
|
||||
reveal_type(X) # revealed: Literal[42]
|
||||
```
|
||||
|
||||
## Dotted
|
||||
|
||||
```py path=package/__init__.py
|
||||
```
|
||||
|
||||
```py path=package/foo/bar/baz.py
|
||||
X = 42
|
||||
```
|
||||
|
||||
```py path=package/bar.py
|
||||
from .foo.bar.baz import X
|
||||
reveal_type(X) # revealed: Literal[42]
|
||||
```
|
||||
|
||||
## Bare to package
|
||||
|
||||
```py path=package/__init__.py
|
||||
X = 42
|
||||
```
|
||||
|
||||
```py path=package/bar.py
|
||||
from . import X
|
||||
reveal_type(X) # revealed: Literal[42]
|
||||
```
|
||||
|
||||
## Non-existent + bare to package
|
||||
|
||||
```py path=package/bar.py
|
||||
from . import X # error: [unresolved-import]
|
||||
reveal_type(X) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Dunder init
|
||||
|
||||
```py path=package/__init__.py
|
||||
from .foo import X
|
||||
reveal_type(X) # revealed: Literal[42]
|
||||
```
|
||||
|
||||
```py path=package/foo.py
|
||||
X = 42
|
||||
```
|
||||
|
||||
## Non-existent + dunder init
|
||||
|
||||
```py path=package/__init__.py
|
||||
from .foo import X # error: [unresolved-import]
|
||||
reveal_type(X) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Long relative import
|
||||
|
||||
```py path=package/__init__.py
|
||||
```
|
||||
|
||||
```py path=package/foo.py
|
||||
X = 42
|
||||
```
|
||||
|
||||
```py path=package/subpackage/subsubpackage/bar.py
|
||||
from ...foo import X
|
||||
reveal_type(X) # revealed: Literal[42]
|
||||
```
|
||||
|
||||
## Unbound symbol
|
||||
|
||||
```py path=package/__init__.py
|
||||
```
|
||||
|
||||
```py path=package/foo.py
|
||||
x
|
||||
```
|
||||
|
||||
```py path=package/bar.py
|
||||
from .foo import x # error: [unresolved-import]
|
||||
reveal_type(x) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Bare to module
|
||||
|
||||
```py path=package/__init__.py
|
||||
```
|
||||
|
||||
```py path=package/foo.py
|
||||
X = 42
|
||||
```
|
||||
|
||||
```py path=package/bar.py
|
||||
# TODO: support submodule imports
|
||||
from . import foo # error: [unresolved-import]
|
||||
y = foo.X
|
||||
|
||||
# TODO: should be `Literal[42]`
|
||||
reveal_type(y) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Non-existent + bare to module
|
||||
|
||||
```py path=package/__init__.py
|
||||
```
|
||||
|
||||
```py path=package/bar.py
|
||||
# TODO: support submodule imports
|
||||
from . import foo # error: [unresolved-import]
|
||||
|
||||
reveal_type(foo) # revealed: Unknown
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
# Stubs
|
||||
|
||||
## Import from stub declaration
|
||||
|
||||
```py
|
||||
from b import x
|
||||
y = x
|
||||
reveal_type(y) # revealed: int
|
||||
```
|
||||
|
||||
```py path=b.pyi
|
||||
x: int
|
||||
```
|
||||
|
||||
## Import from non-stub with declaration and definition
|
||||
|
||||
```py
|
||||
from b import x
|
||||
y = x
|
||||
reveal_type(y) # revealed: int
|
||||
```
|
||||
|
||||
```py path=b.py
|
||||
x: int = 1
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
# Boolean literals
|
||||
|
||||
```py
|
||||
x = True
|
||||
y = False
|
||||
reveal_type(x) # revealed: Literal[True]
|
||||
reveal_type(y) # revealed: Literal[False]
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
# Dictionaries
|
||||
|
||||
## Empty dictionary
|
||||
|
||||
```py
|
||||
x = {}
|
||||
reveal_type(x) # revealed: dict
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
# Lists
|
||||
|
||||
## Empty list
|
||||
|
||||
```py
|
||||
x = []
|
||||
reveal_type(x) # revealed: list
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
# Sets
|
||||
|
||||
## Basic set
|
||||
|
||||
```py
|
||||
x = {1, 2}
|
||||
reveal_type(x) # revealed: set
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
# Tuples
|
||||
|
||||
## Empty tuple
|
||||
|
||||
```py
|
||||
x = ()
|
||||
reveal_type(x) # revealed: tuple[()]
|
||||
```
|
||||
|
||||
## Heterogeneous tuple
|
||||
|
||||
```py
|
||||
x = (1, 'a')
|
||||
y = (1, (2, 3))
|
||||
z = (x, 2)
|
||||
|
||||
reveal_type(x) # revealed: tuple[Literal[1], Literal["a"]]
|
||||
reveal_type(y) # revealed: tuple[Literal[1], tuple[Literal[2], Literal[3]]]
|
||||
reveal_type(z) # revealed: tuple[tuple[Literal[1], Literal["a"]], Literal[2]]
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
# Complex literals
|
||||
|
||||
## Complex numbers
|
||||
|
||||
```py
|
||||
reveal_type(2j) # revealed: complex
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
# f-strings
|
||||
|
||||
## Expression
|
||||
|
||||
```py
|
||||
x = 0
|
||||
y = str()
|
||||
z = False
|
||||
|
||||
a = f'hello'
|
||||
b = f'h {x}'
|
||||
c = 'one ' f'single ' f'literal'
|
||||
d = 'first ' f'second({b})' f' third'
|
||||
e = f'-{y}-'
|
||||
f = f'-{y}-' f'--' '--'
|
||||
g = f'{z} == {False} is {True}'
|
||||
|
||||
reveal_type(a) # revealed: Literal["hello"]
|
||||
reveal_type(b) # revealed: Literal["h 0"]
|
||||
reveal_type(c) # revealed: Literal["one single literal"]
|
||||
reveal_type(d) # revealed: Literal["first second(h 0) third"]
|
||||
reveal_type(e) # revealed: str
|
||||
reveal_type(f) # revealed: str
|
||||
reveal_type(g) # revealed: Literal["False == False is True"]
|
||||
```
|
||||
|
||||
## Conversion Flags
|
||||
|
||||
```py
|
||||
string = 'hello'
|
||||
a = f'{string!r}'
|
||||
|
||||
# TODO: should be `Literal["'hello'"]`
|
||||
reveal_type(a) # revealed: str
|
||||
```
|
||||
|
||||
## Format Specifiers
|
||||
|
||||
```py
|
||||
a = f'{1:02}'
|
||||
|
||||
# TODO: should be `Literal["01"]`
|
||||
reveal_type(a) # revealed: str
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
# Float literals
|
||||
|
||||
## Basic
|
||||
|
||||
```py
|
||||
reveal_type(1.0) # revealed: float
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
# Integer literals
|
||||
|
||||
## Literals
|
||||
|
||||
We can infer an integer literal type:
|
||||
|
||||
```py
|
||||
reveal_type(1) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
## Variable
|
||||
|
||||
```py
|
||||
x = 1
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
## Overflow
|
||||
|
||||
We only track integer literals within the range of an i64:
|
||||
|
||||
```py
|
||||
reveal_type(9223372036854775808) # revealed: int
|
||||
```
|
||||
|
||||
## Big int
|
||||
|
||||
We don't support big integer literals; we just infer `int` type instead:
|
||||
|
||||
```py
|
||||
x = 10_000_000_000_000_000_000
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
## Negated
|
||||
|
||||
```py
|
||||
x = -1
|
||||
y = -1234567890987654321
|
||||
z = --987
|
||||
reveal_type(x) # revealed: Literal[-1]
|
||||
reveal_type(y) # revealed: Literal[-1234567890987654321]
|
||||
reveal_type(z) # revealed: Literal[987]
|
||||
```
|
||||
|
||||
## Floats
|
||||
|
||||
```py
|
||||
reveal_type(1.0) # revealed: float
|
||||
```
|
||||
|
||||
## Complex
|
||||
|
||||
```py
|
||||
reveal_type(2j) # revealed: complex
|
||||
```
|
||||
@@ -0,0 +1,26 @@
|
||||
# String literals
|
||||
|
||||
## Simple
|
||||
|
||||
```py
|
||||
w = "Hello"
|
||||
x = 'world'
|
||||
y = "Guten " + 'tag'
|
||||
z = 'bon ' + "jour"
|
||||
|
||||
reveal_type(w) # revealed: Literal["Hello"]
|
||||
reveal_type(x) # revealed: Literal["world"]
|
||||
reveal_type(y) # revealed: Literal["Guten tag"]
|
||||
reveal_type(z) # revealed: Literal["bon jour"]
|
||||
```
|
||||
|
||||
## Nested Quotes
|
||||
|
||||
```py
|
||||
x = 'I say "hello" to you'
|
||||
y = "You say \"hey\" back"
|
||||
z = 'No "closure here'
|
||||
reveal_type(x) # revealed: Literal["I say \"hello\" to you"]
|
||||
reveal_type(y) # revealed: Literal["You say \"hey\" back"]
|
||||
reveal_type(z) # revealed: Literal["No \"closure here"]
|
||||
```
|
||||
@@ -0,0 +1,41 @@
|
||||
# Async
|
||||
|
||||
Async `for` loops do not work according to the synchronous iteration protocol.
|
||||
|
||||
## Invalid async for loop
|
||||
|
||||
```py
|
||||
async def foo():
|
||||
class Iterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
class Iterable:
|
||||
def __iter__(self) -> Iterator:
|
||||
return Iterator()
|
||||
|
||||
async for x in Iterator():
|
||||
pass
|
||||
|
||||
# TODO
|
||||
reveal_type(x) # revealed: Unbound | @Todo
|
||||
```
|
||||
|
||||
## Basic async for loop
|
||||
|
||||
```py
|
||||
async def foo():
|
||||
class IntAsyncIterator:
|
||||
async def __anext__(self) -> int:
|
||||
return 42
|
||||
|
||||
class IntAsyncIterable:
|
||||
def __aiter__(self) -> IntAsyncIterator:
|
||||
return IntAsyncIterator()
|
||||
|
||||
#TODO(Alex): async iterables/iterators!
|
||||
async for x in IntAsyncIterable():
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: Unbound | @Todo
|
||||
```
|
||||
@@ -0,0 +1,134 @@
|
||||
# For loops
|
||||
|
||||
## Basic `for` loop
|
||||
|
||||
```py
|
||||
class IntIterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
class IntIterable:
|
||||
def __iter__(self) -> IntIterator:
|
||||
return IntIterator()
|
||||
|
||||
for x in IntIterable():
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: Unbound | int
|
||||
```
|
||||
|
||||
## With previous definition
|
||||
|
||||
```py
|
||||
class IntIterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
class IntIterable:
|
||||
def __iter__(self) -> IntIterator:
|
||||
return IntIterator()
|
||||
|
||||
x = 'foo'
|
||||
|
||||
for x in IntIterable():
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: Literal["foo"] | int
|
||||
```
|
||||
|
||||
## With `else` (no break)
|
||||
|
||||
```py
|
||||
class IntIterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
class IntIterable:
|
||||
def __iter__(self) -> IntIterator:
|
||||
return IntIterator()
|
||||
|
||||
for x in IntIterable():
|
||||
pass
|
||||
else:
|
||||
x = 'foo'
|
||||
|
||||
reveal_type(x) # revealed: Literal["foo"]
|
||||
```
|
||||
|
||||
## May `break`
|
||||
|
||||
```py
|
||||
class IntIterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
class IntIterable:
|
||||
def __iter__(self) -> IntIterator:
|
||||
return IntIterator()
|
||||
|
||||
for x in IntIterable():
|
||||
if x > 5:
|
||||
break
|
||||
else:
|
||||
x = 'foo'
|
||||
|
||||
reveal_type(x) # revealed: int | Literal["foo"]
|
||||
```
|
||||
|
||||
## With old-style iteration protocol
|
||||
|
||||
```py
|
||||
class OldStyleIterable:
|
||||
def __getitem__(self, key: int) -> int:
|
||||
return 42
|
||||
|
||||
for x in OldStyleIterable():
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: Unbound | int
|
||||
```
|
||||
|
||||
## With heterogeneous tuple
|
||||
|
||||
```py
|
||||
for x in (1, 'a', b'foo'):
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: Unbound | Literal[1] | Literal["a"] | Literal[b"foo"]
|
||||
```
|
||||
|
||||
## With non-callable iterator
|
||||
|
||||
```py
|
||||
class NotIterable:
|
||||
if flag:
|
||||
__iter__ = 1
|
||||
else:
|
||||
__iter__ = None
|
||||
|
||||
for x in NotIterable(): # error: "Object of type `NotIterable` is not iterable"
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: Unbound | Unknown
|
||||
```
|
||||
|
||||
## Invalid iterable
|
||||
|
||||
```py
|
||||
nonsense = 123
|
||||
for x in nonsense: # error: "Object of type `Literal[123]` is not iterable"
|
||||
pass
|
||||
```
|
||||
|
||||
## New over old style iteration protocol
|
||||
|
||||
```py
|
||||
class NotIterable:
|
||||
def __getitem__(self, key: int) -> int:
|
||||
return 42
|
||||
|
||||
__iter__ = None
|
||||
|
||||
for x in NotIterable(): # error: "Object of type `NotIterable` is not iterable"
|
||||
pass
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
# Iterators
|
||||
|
||||
## Yield must be iterable
|
||||
|
||||
```py
|
||||
class NotIterable: pass
|
||||
|
||||
class Iterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
class Iterable:
|
||||
def __iter__(self) -> Iterator: ...
|
||||
|
||||
def generator_function():
|
||||
yield from Iterable()
|
||||
yield from NotIterable() # error: "Object of type `NotIterable` is not iterable"
|
||||
```
|
||||
@@ -0,0 +1,43 @@
|
||||
# While loops
|
||||
|
||||
## Basic While Loop
|
||||
|
||||
```py
|
||||
x = 1
|
||||
while flag:
|
||||
x = 2
|
||||
|
||||
reveal_type(x) # revealed: Literal[1, 2]
|
||||
```
|
||||
|
||||
## While with else (no break)
|
||||
|
||||
```py
|
||||
x = 1
|
||||
while flag:
|
||||
x = 2
|
||||
else:
|
||||
y = x
|
||||
x = 3
|
||||
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
reveal_type(y) # revealed: Literal[1, 2]
|
||||
```
|
||||
|
||||
## While with Else (may break)
|
||||
|
||||
```py
|
||||
x = 1
|
||||
y = 0
|
||||
while flag:
|
||||
x = 2
|
||||
if flag2:
|
||||
y = 4
|
||||
break
|
||||
else:
|
||||
y = x
|
||||
x = 3
|
||||
|
||||
reveal_type(x) # revealed: Literal[2, 3]
|
||||
reveal_type(y) # revealed: Literal[1, 2, 4]
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
# Narrowing for `is` conditionals
|
||||
|
||||
## `is None`
|
||||
|
||||
```py
|
||||
x = None if flag else 1
|
||||
|
||||
if x is None:
|
||||
# TODO the following should be simplified to 'None'
|
||||
reveal_type(x) # revealed: None | Literal[1] & None
|
||||
|
||||
reveal_type(x) # revealed: None | Literal[1]
|
||||
```
|
||||
|
||||
## `is` for other types
|
||||
|
||||
```py
|
||||
class A:
|
||||
...
|
||||
|
||||
x = A()
|
||||
y = x if flag else None
|
||||
|
||||
if y is x:
|
||||
# TODO the following should be simplified to 'A'
|
||||
reveal_type(y) # revealed: A | None & A
|
||||
|
||||
reveal_type(y) # revealed: A | None
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
# Narrowing for `is not` conditionals
|
||||
|
||||
## `is not None`
|
||||
|
||||
The type guard removes `None` from the union type:
|
||||
|
||||
```py
|
||||
x = None if flag else 1
|
||||
|
||||
if x is not None:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
|
||||
reveal_type(x) # revealed: None | Literal[1]
|
||||
```
|
||||
|
||||
## `is not` for other singleton types
|
||||
|
||||
```py
|
||||
x = True if flag else False
|
||||
reveal_type(x) # revealed: bool
|
||||
|
||||
if x is not False:
|
||||
# TODO the following should be `Literal[True]`
|
||||
reveal_type(x) # revealed: bool & ~Literal[False]
|
||||
```
|
||||
|
||||
## `is not` for non-singleton types
|
||||
|
||||
Non-singleton types should *not* narrow the type: two instances of a
|
||||
non-singleton class may occupy different addresses in memory even if
|
||||
they compare equal.
|
||||
|
||||
```py
|
||||
x = 345
|
||||
y = 345
|
||||
|
||||
if x is not y:
|
||||
reveal_type(x) # revealed: Literal[345]
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
# Narrowing for `match` statements
|
||||
|
||||
## Single `match` pattern
|
||||
|
||||
```py
|
||||
x = None if flag else 1
|
||||
reveal_type(x) # revealed: None | Literal[1]
|
||||
|
||||
y = 0
|
||||
|
||||
match x:
|
||||
case None:
|
||||
y = x
|
||||
|
||||
# TODO intersection simplification: should be just Literal[0] | None
|
||||
reveal_type(y) # revealed: Literal[0] | None | Literal[1] & None
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
# `is not None` narrowing
|
||||
|
||||
```py
|
||||
x = None if flag else 1
|
||||
if x is not None:
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
|
||||
reveal_type(x) # revealed: None | Literal[1]
|
||||
```
|
||||
@@ -1,35 +0,0 @@
|
||||
# Numbers
|
||||
|
||||
## Integers
|
||||
|
||||
### Literals
|
||||
|
||||
We can infer an integer literal type:
|
||||
|
||||
```py
|
||||
reveal_type(1) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
### Overflow
|
||||
|
||||
We only track integer literals within the range of an i64:
|
||||
|
||||
```py
|
||||
reveal_type(9223372036854775808) # revealed: int
|
||||
```
|
||||
|
||||
## Floats
|
||||
|
||||
There aren't literal float types, but we infer the general float type:
|
||||
|
||||
```py
|
||||
reveal_type(1.0) # revealed: float
|
||||
```
|
||||
|
||||
## Complex
|
||||
|
||||
Same for complex:
|
||||
|
||||
```py
|
||||
reveal_type(2j) # revealed: complex
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
# Builtin scope
|
||||
|
||||
## Conditionally global or builtin
|
||||
|
||||
If a builtin name is conditionally defined as a global, a name lookup should union the builtin type
|
||||
with the conditionally-defined type:
|
||||
|
||||
```py
|
||||
def returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
if returns_bool():
|
||||
copyright = 1
|
||||
|
||||
def f():
|
||||
reveal_type(copyright) # revealed: Literal[copyright] | Literal[1]
|
||||
```
|
||||
|
||||
## Conditionally global or builtin, with annotation
|
||||
|
||||
Same is true if the name is annotated:
|
||||
|
||||
```py
|
||||
def returns_bool() -> bool:
|
||||
return True
|
||||
|
||||
if returns_bool():
|
||||
copyright: int = 1
|
||||
|
||||
def f():
|
||||
reveal_type(copyright) # revealed: Literal[copyright] | int
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
# Classes shadowing
|
||||
|
||||
## Implicit error
|
||||
|
||||
```py
|
||||
class C: pass
|
||||
C = 1 # error: "Implicit shadowing of class `C`; annotate to make it explicit if this is intentional"
|
||||
```
|
||||
|
||||
## Explicit
|
||||
|
||||
No diagnostic is raised in the case of explicit shadowing:
|
||||
|
||||
```py
|
||||
class C: pass
|
||||
C: int = 1
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
# Function shadowing
|
||||
|
||||
## Parameter
|
||||
|
||||
Parameter `x` of type `str` is shadowed and reassigned with a new `int` value inside the function. No diagnostics should be generated.
|
||||
|
||||
```py path=a.py
|
||||
def f(x: str):
|
||||
x: int = int(x)
|
||||
```
|
||||
|
||||
## Implicit error
|
||||
|
||||
```py path=a.py
|
||||
def f(): pass
|
||||
f = 1 # error: "Implicit shadowing of function `f`; annotate to make it explicit if this is intentional"
|
||||
```
|
||||
|
||||
## Explicit shadowing
|
||||
|
||||
```py path=a.py
|
||||
def f(): pass
|
||||
f: int = 1
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
# Shadwing declaration
|
||||
|
||||
## Shadow after incompatible declarations is OK
|
||||
|
||||
```py
|
||||
if flag:
|
||||
x: str
|
||||
else:
|
||||
x: int
|
||||
x: bytes = b'foo'
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
# Class defenitions in stubs
|
||||
|
||||
## Cyclical class definition
|
||||
|
||||
In type stubs, classes can reference themselves in their base class definitions. For example, in `typeshed`, we have `class str(Sequence[str]): ...`.
|
||||
|
||||
```py path=a.pyi
|
||||
class C(C): ...
|
||||
reveal_type(C) # revealed: Literal[C]
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# Bytes subscript
|
||||
|
||||
## Simple
|
||||
|
||||
```py
|
||||
w = b'red' b'knot'
|
||||
x = b'hello'
|
||||
y = b'world' + b'!'
|
||||
z = b'\xff\x00'
|
||||
|
||||
reveal_type(w) # revealed: Literal[b"redknot"]
|
||||
reveal_type(x) # revealed: Literal[b"hello"]
|
||||
reveal_type(y) # revealed: Literal[b"world!"]
|
||||
reveal_type(z) # revealed: Literal[b"\xff\x00"]
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
# Class subscript
|
||||
|
||||
## Class getitem unbound
|
||||
|
||||
```py
|
||||
class NotSubscriptable: pass
|
||||
a = NotSubscriptable[0] # error: "Cannot subscript object of type `Literal[NotSubscriptable]` with no `__class_getitem__` method"
|
||||
```
|
||||
|
||||
## Class getitem
|
||||
|
||||
```py
|
||||
class Identity:
|
||||
def __class_getitem__(cls, item: int) -> str:
|
||||
return item
|
||||
|
||||
a = Identity[0]
|
||||
reveal_type(a) # revealed: str
|
||||
```
|
||||
|
||||
## Class getitem union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
|
||||
class Identity:
|
||||
if flag:
|
||||
def __class_getitem__(cls, item: int) -> str:
|
||||
return item
|
||||
else:
|
||||
def __class_getitem__(cls, item: int) -> int:
|
||||
return item
|
||||
|
||||
a = Identity[0]
|
||||
reveal_type(a) # revealed: str | int
|
||||
```
|
||||
|
||||
## Class getitem with class union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
|
||||
class Identity1:
|
||||
def __class_getitem__(cls, item: int) -> str:
|
||||
return item
|
||||
|
||||
class Identity2:
|
||||
def __class_getitem__(cls, item: int) -> int:
|
||||
return item
|
||||
|
||||
if flag:
|
||||
a = Identity1
|
||||
else:
|
||||
a = Identity2
|
||||
|
||||
b = a[0]
|
||||
reveal_type(a) # revealed: Literal[Identity1, Identity2]
|
||||
reveal_type(b) # revealed: str | int
|
||||
```
|
||||
|
||||
## Class getitem with unbound method union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
|
||||
if flag:
|
||||
class Identity:
|
||||
def __class_getitem__(self, x: int) -> str:
|
||||
pass
|
||||
else:
|
||||
class Identity: pass
|
||||
|
||||
a = Identity[42] # error: [call-non-callable] "Method `__class_getitem__` of type `Literal[__class_getitem__] | Unbound` is not callable on object of type `Literal[Identity, Identity]`"
|
||||
reveal_type(a) # revealed: str | Unknown
|
||||
```
|
||||
|
||||
## TODO: Class getitem non-class union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
|
||||
if flag:
|
||||
class Identity:
|
||||
def __class_getitem__(self, x: int) -> str:
|
||||
pass
|
||||
else:
|
||||
Identity = 1
|
||||
|
||||
a = Identity[42] # error: "Cannot subscript object of type `Literal[Identity] | Literal[1]` with no `__getitem__` method"
|
||||
# TODO: should _probably_ emit `str | Unknown`
|
||||
reveal_type(a) # revealed: Unknown
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
# Instance subscript
|
||||
|
||||
## Getitem unbound
|
||||
|
||||
```py
|
||||
class NotSubscriptable: pass
|
||||
a = NotSubscriptable()[0] # error: "Cannot subscript object of type `NotSubscriptable` with no `__getitem__` method"
|
||||
```
|
||||
|
||||
## Getitem not callable
|
||||
|
||||
```py
|
||||
class NotSubscriptable:
|
||||
__getitem__ = None
|
||||
|
||||
a = NotSubscriptable()[0] # error: "Method `__getitem__` of type `None` is not callable on object of type `NotSubscriptable`"
|
||||
```
|
||||
|
||||
## Valid getitem
|
||||
|
||||
```py
|
||||
class Identity:
|
||||
def __getitem__(self, index: int) -> int:
|
||||
return index
|
||||
|
||||
a = Identity()[0]
|
||||
reveal_type(a) # revealed: int
|
||||
```
|
||||
|
||||
## Getitem union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
|
||||
class Identity:
|
||||
if flag:
|
||||
def __getitem__(self, index: int) -> int:
|
||||
return index
|
||||
else:
|
||||
def __getitem__(self, index: int) -> str:
|
||||
return str(index)
|
||||
|
||||
a = Identity()[0]
|
||||
reveal_type(a) # revealed: int | str
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
# List subscripts
|
||||
|
||||
## Indexing into lists
|
||||
|
||||
A list can be indexed into with:
|
||||
|
||||
- numbers
|
||||
- slices
|
||||
|
||||
```py
|
||||
x = [1, 2, 3]
|
||||
reveal_type(x) # revealed: list
|
||||
# TODO reveal int
|
||||
reveal_type(x[0]) # revealed: @Todo
|
||||
# TODO reveal list
|
||||
reveal_type(x[0:1]) # revealed: @Todo
|
||||
# TODO error
|
||||
reveal_type(x["a"]) # revealed: @Todo
|
||||
```
|
||||
|
||||
## Assignments within list assignment
|
||||
|
||||
In assignment, we might also have a named assignment.
|
||||
This should also get type checked.
|
||||
|
||||
```py
|
||||
x = [1, 2, 3]
|
||||
x[0 if (y := 2) else 1] = 5
|
||||
# TODO error? (indeterminite index type)
|
||||
x["a" if (y := 2) else 1] = 6
|
||||
# TODO error (can't index via string)
|
||||
x["a" if (y := 2) else "b"] = 6
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
# Subscript on strings
|
||||
|
||||
## Simple
|
||||
|
||||
```py
|
||||
s = 'abcde'
|
||||
|
||||
a = s[0]
|
||||
b = s[1]
|
||||
c = s[-1]
|
||||
d = s[-2]
|
||||
e = s[8] # error: [index-out-of-bounds] "Index 8 is out of bounds for string `Literal["abcde"]` with length 5"
|
||||
f = s[-8] # error: [index-out-of-bounds] "Index -8 is out of bounds for string `Literal["abcde"]` with length 5"
|
||||
|
||||
reveal_type(a) # revealed: Literal["a"]
|
||||
reveal_type(b) # revealed: Literal["b"]
|
||||
reveal_type(c) # revealed: Literal["e"]
|
||||
reveal_type(d) # revealed: Literal["d"]
|
||||
reveal_type(e) # revealed: Unknown
|
||||
reveal_type(f) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Function return
|
||||
|
||||
```py
|
||||
def add(x: int, y: int) -> int:
|
||||
return x + y
|
||||
|
||||
a = 'abcde'[add(0, 1)]
|
||||
# TODO: Support overloads... Should be `str`
|
||||
reveal_type(a) # revealed: @Todo
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
# Tuple subscripts
|
||||
|
||||
## Basic
|
||||
|
||||
```py
|
||||
t = (1, 'a', 'b')
|
||||
|
||||
a = t[0]
|
||||
b = t[1]
|
||||
c = t[-1]
|
||||
d = t[-2]
|
||||
e = t[4] # error: [index-out-of-bounds]
|
||||
f = t[-4] # error: [index-out-of-bounds]
|
||||
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal["a"]
|
||||
reveal_type(c) # revealed: Literal["b"]
|
||||
reveal_type(d) # revealed: Literal["a"]
|
||||
reveal_type(e) # revealed: Unknown
|
||||
reveal_type(f) # revealed: Unknown
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# Unary Operations
|
||||
|
||||
## Unary Addition
|
||||
|
||||
```py
|
||||
a = +0
|
||||
b = +1
|
||||
c = +True
|
||||
|
||||
reveal_type(a) # revealed: Literal[0]
|
||||
reveal_type(b) # revealed: Literal[1]
|
||||
reveal_type(c) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
## Unary Subtraction
|
||||
|
||||
```py
|
||||
a = -0
|
||||
b = -1
|
||||
c = -True
|
||||
|
||||
reveal_type(a) # revealed: Literal[0]
|
||||
reveal_type(b) # revealed: Literal[-1]
|
||||
reveal_type(c) # revealed: Literal[-1]
|
||||
```
|
||||
|
||||
## Unary Bitwise Inversion
|
||||
|
||||
```py
|
||||
a = ~0
|
||||
b = ~1
|
||||
c = ~True
|
||||
|
||||
reveal_type(a) # revealed: Literal[-1]
|
||||
reveal_type(b) # revealed: Literal[-2]
|
||||
reveal_type(c) # revealed: Literal[-2]
|
||||
```
|
||||
149
crates/red_knot_python_semantic/resources/mdtest/unary/not.md
Normal file
149
crates/red_knot_python_semantic/resources/mdtest/unary/not.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# Unary not
|
||||
|
||||
## None
|
||||
|
||||
```py
|
||||
a = not None
|
||||
b = not not None
|
||||
reveal_type(a) # revealed: Literal[True]
|
||||
reveal_type(b) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Function
|
||||
|
||||
```py
|
||||
from typing import reveal_type
|
||||
|
||||
def f():
|
||||
return 1
|
||||
|
||||
a = not f
|
||||
b = not reveal_type
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
# TODO Unknown should not be part of the type of typing.reveal_type
|
||||
# reveal_type(b) revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Module
|
||||
|
||||
```py
|
||||
import b; import warnings
|
||||
|
||||
x = not b
|
||||
z = not warnings
|
||||
|
||||
reveal_type(x) # revealed: Literal[False]
|
||||
reveal_type(z) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
```py path=b.py
|
||||
y = 1
|
||||
```
|
||||
|
||||
## Union
|
||||
|
||||
```py
|
||||
if flag:
|
||||
p = 1
|
||||
q = 3.3
|
||||
r = "hello"
|
||||
s = "world"
|
||||
t = 0
|
||||
else:
|
||||
p = "hello"
|
||||
q = 4
|
||||
r = ""
|
||||
s = 0
|
||||
t = ""
|
||||
|
||||
a = not p
|
||||
b = not q
|
||||
c = not r
|
||||
d = not s
|
||||
e = not t
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: bool
|
||||
reveal_type(c) # revealed: bool
|
||||
reveal_type(d) # revealed: bool
|
||||
reveal_type(e) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
## Integer literal
|
||||
|
||||
```py
|
||||
a = not 1
|
||||
b = not 1234567890987654321
|
||||
e = not 0
|
||||
x = not -1
|
||||
y = not -1234567890987654321
|
||||
z = not --987
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: Literal[False]
|
||||
reveal_type(e) # revealed: Literal[True]
|
||||
reveal_type(x) # revealed: Literal[False]
|
||||
reveal_type(y) # revealed: Literal[False]
|
||||
reveal_type(z) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Boolean literal
|
||||
|
||||
```py
|
||||
w = True
|
||||
x = False
|
||||
y = not w
|
||||
z = not x
|
||||
|
||||
reveal_type(w) # revealed: Literal[True]
|
||||
reveal_type(x) # revealed: Literal[False]
|
||||
reveal_type(y) # revealed: Literal[False]
|
||||
reveal_type(z) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
## String literal
|
||||
|
||||
```py
|
||||
a = not "hello"
|
||||
b = not ""
|
||||
c = not "0"
|
||||
d = not "hello" + "world"
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: Literal[True]
|
||||
reveal_type(c) # revealed: Literal[False]
|
||||
reveal_type(d) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Bytes literal
|
||||
|
||||
```py
|
||||
a = not b"hello"
|
||||
b = not b""
|
||||
c = not b"0"
|
||||
d = not b"hello" + b"world"
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: Literal[True]
|
||||
reveal_type(c) # revealed: Literal[False]
|
||||
reveal_type(d) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Tuple
|
||||
|
||||
```py
|
||||
a = not (1,)
|
||||
b = not (1, 2)
|
||||
c = not (1, 2, 3)
|
||||
d = not ()
|
||||
e = not ("hello",)
|
||||
f = not (1, "hello")
|
||||
|
||||
reveal_type(a) # revealed: Literal[False]
|
||||
reveal_type(b) # revealed: Literal[False]
|
||||
reveal_type(c) # revealed: Literal[False]
|
||||
reveal_type(d) # revealed: Literal[True]
|
||||
reveal_type(e) # revealed: Literal[False]
|
||||
reveal_type(f) # revealed: Literal[False]
|
||||
```
|
||||
273
crates/red_knot_python_semantic/resources/mdtest/unpacking.md
Normal file
273
crates/red_knot_python_semantic/resources/mdtest/unpacking.md
Normal file
@@ -0,0 +1,273 @@
|
||||
# Unpacking
|
||||
|
||||
## Tuple
|
||||
|
||||
### Simple tuple
|
||||
|
||||
```py
|
||||
(a, b, c) = (1, 2, 3)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
reveal_type(c) # revealed: Literal[3]
|
||||
```
|
||||
|
||||
### Simple list
|
||||
|
||||
```py
|
||||
[a, b, c] = (1, 2, 3)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
reveal_type(c) # revealed: Literal[3]
|
||||
```
|
||||
|
||||
### Simple mixed
|
||||
|
||||
```py
|
||||
[a, (b, c), d] = (1, (2, 3), 4)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
reveal_type(c) # revealed: Literal[3]
|
||||
reveal_type(d) # revealed: Literal[4]
|
||||
```
|
||||
|
||||
### Multiple assignment
|
||||
|
||||
```py
|
||||
a, b = c = 1, 2
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
reveal_type(c) # revealed: tuple[Literal[1], Literal[2]]
|
||||
```
|
||||
|
||||
### Nested tuple with unpacking
|
||||
|
||||
```py
|
||||
(a, (b, c), d) = (1, (2, 3), 4)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
reveal_type(c) # revealed: Literal[3]
|
||||
reveal_type(d) # revealed: Literal[4]
|
||||
```
|
||||
|
||||
### Nested tuple without unpacking
|
||||
|
||||
```py
|
||||
(a, b, c) = (1, (2, 3), 4)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: tuple[Literal[2], Literal[3]]
|
||||
reveal_type(c) # revealed: Literal[4]
|
||||
```
|
||||
|
||||
### Uneven unpacking (1)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (there aren't enough values to unpack)
|
||||
(a, b, c) = (1, 2)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
reveal_type(c) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Uneven unpacking (2)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (too many values to unpack)
|
||||
(a, b) = (1, 2, 3)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
### Starred expression (1)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (need more values to unpack)
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
[a, *b, c, d] = (1, 2) # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
# TODO: Should be list[Any] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: Literal[2]
|
||||
reveal_type(d) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Starred expression (2)
|
||||
|
||||
```py
|
||||
[a, *b, c] = (1, 2) # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
# TODO: Should be list[Any] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
### Starred expression (3)
|
||||
|
||||
```py
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
[a, *b, c] = (1, 2, 3) # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
# TODO: Should be list[int] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: Literal[3]
|
||||
```
|
||||
|
||||
### Starred expression (4)
|
||||
|
||||
```py
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
[a, *b, c, d] = (1, 2, 3, 4, 5, 6) # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
# TODO: Should be list[int] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: Literal[5]
|
||||
reveal_type(d) # revealed: Literal[6]
|
||||
```
|
||||
|
||||
### Starred expression (5)
|
||||
|
||||
```py
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
[a, b, *c] = (1, 2, 3, 4) # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: Literal[2]
|
||||
# TODO: Should be list[int] once support for assigning to starred expression is added
|
||||
reveal_type(c) # revealed: @Todo
|
||||
```
|
||||
|
||||
### Non-iterable unpacking
|
||||
|
||||
TODO: Remove duplicate diagnostics. This is happening because for a sequence-like
|
||||
assignment target, multiple definitions are created and the inference engine runs
|
||||
on each of them which results in duplicate diagnostics.
|
||||
|
||||
```py
|
||||
# error: "Object of type `Literal[1]` is not iterable"
|
||||
# error: "Object of type `Literal[1]` is not iterable"
|
||||
a, b = 1
|
||||
reveal_type(a) # revealed: Unknown
|
||||
reveal_type(b) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Custom iterator unpacking
|
||||
|
||||
```py
|
||||
class Iterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
|
||||
class Iterable:
|
||||
def __iter__(self) -> Iterator:
|
||||
return Iterator()
|
||||
|
||||
|
||||
(a, b) = Iterable()
|
||||
reveal_type(a) # revealed: int
|
||||
reveal_type(b) # revealed: int
|
||||
```
|
||||
|
||||
### Custom iterator unpacking nested
|
||||
|
||||
```py
|
||||
class Iterator:
|
||||
def __next__(self) -> int:
|
||||
return 42
|
||||
|
||||
|
||||
class Iterable:
|
||||
def __iter__(self) -> Iterator:
|
||||
return Iterator()
|
||||
|
||||
|
||||
(a, (b, c), d) = (1, Iterable(), 2)
|
||||
reveal_type(a) # revealed: Literal[1]
|
||||
reveal_type(b) # revealed: int
|
||||
reveal_type(c) # revealed: int
|
||||
reveal_type(d) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
## String
|
||||
|
||||
### Simple unpacking
|
||||
|
||||
```py
|
||||
a, b = 'ab'
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: LiteralString
|
||||
```
|
||||
|
||||
### Uneven unpacking (1)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (there aren't enough values to unpack)
|
||||
a, b, c = 'ab'
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: LiteralString
|
||||
reveal_type(c) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Uneven unpacking (2)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (too many values to unpack)
|
||||
a, b = 'abc'
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: LiteralString
|
||||
```
|
||||
|
||||
### Starred expression (1)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (need more values to unpack)
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
(a, *b, c, d) = "ab" # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: LiteralString
|
||||
reveal_type(d) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Starred expression (2)
|
||||
|
||||
```py
|
||||
(a, *b, c) = "ab" # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
# TODO: Should be list[Any] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: LiteralString
|
||||
```
|
||||
|
||||
### Starred expression (3)
|
||||
|
||||
```py
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
(a, *b, c) = "abc" # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: LiteralString
|
||||
```
|
||||
|
||||
### Starred expression (4)
|
||||
|
||||
```py
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
(a, *b, c, d) = "abcdef" # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
|
||||
reveal_type(b) # revealed: @Todo
|
||||
reveal_type(c) # revealed: LiteralString
|
||||
reveal_type(d) # revealed: LiteralString
|
||||
```
|
||||
|
||||
### Starred expression (5)
|
||||
|
||||
```py
|
||||
# TODO: Remove 'not-iterable' diagnostic
|
||||
(a, b, *c) = "abcd" # error: "Object of type `None` is not iterable"
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: LiteralString
|
||||
# TODO: Should be list[int] once support for assigning to starred expression is added
|
||||
reveal_type(c) # revealed: @Todo
|
||||
```
|
||||
@@ -17,7 +17,7 @@ use super::module::{Module, ModuleKind};
|
||||
use super::path::{ModulePath, SearchPath, SearchPathValidationError};
|
||||
|
||||
/// Resolves a module name to a module.
|
||||
pub fn resolve_module(db: &dyn Db, module_name: ModuleName) -> Option<Module> {
|
||||
pub fn resolve_module(db: &dyn Db, module_name: &ModuleName) -> Option<Module> {
|
||||
let interned_name = ModuleNameIngredient::new(db, module_name);
|
||||
|
||||
resolve_module_query(db, interned_name)
|
||||
@@ -103,7 +103,7 @@ pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
|
||||
// If it doesn't, then that means that multiple modules have the same name in different
|
||||
// root paths, but that the module corresponding to `path` is in a lower priority search path,
|
||||
// in which case we ignore it.
|
||||
let module = resolve_module(db, module_name)?;
|
||||
let module = resolve_module(db, &module_name)?;
|
||||
|
||||
if file == module.file() {
|
||||
Some(module)
|
||||
@@ -728,11 +728,11 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Some(&foo_module),
|
||||
resolve_module(&db, foo_module_name.clone()).as_ref()
|
||||
resolve_module(&db, &foo_module_name).as_ref()
|
||||
);
|
||||
|
||||
assert_eq!("foo", foo_module.name());
|
||||
@@ -755,7 +755,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
|
||||
let builtins = resolve_module(&db, builtins_module_name).expect("builtins to resolve");
|
||||
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
|
||||
|
||||
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi"));
|
||||
}
|
||||
@@ -776,7 +776,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
|
||||
let builtins = resolve_module(&db, builtins_module_name).expect("builtins to resolve");
|
||||
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
|
||||
|
||||
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi"));
|
||||
}
|
||||
@@ -794,11 +794,11 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let functools_module_name = ModuleName::new_static("functools").unwrap();
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Some(&functools_module),
|
||||
resolve_module(&db, functools_module_name).as_ref()
|
||||
resolve_module(&db, &functools_module_name).as_ref()
|
||||
);
|
||||
|
||||
assert_eq!(&stdlib, functools_module.search_path());
|
||||
@@ -848,7 +848,7 @@ mod tests {
|
||||
|
||||
let existing_modules = create_module_names(&["asyncio", "functools", "xml.etree"]);
|
||||
for module_name in existing_modules {
|
||||
let resolved_module = resolve_module(&db, module_name.clone()).unwrap_or_else(|| {
|
||||
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
|
||||
panic!("Expected module {module_name} to exist in the mock stdlib")
|
||||
});
|
||||
let search_path = resolved_module.search_path();
|
||||
@@ -901,7 +901,7 @@ mod tests {
|
||||
|
||||
for module_name in nonexisting_modules {
|
||||
assert!(
|
||||
resolve_module(&db, module_name.clone()).is_none(),
|
||||
resolve_module(&db, &module_name).is_none(),
|
||||
"Unexpectedly resolved a module for {module_name}"
|
||||
);
|
||||
}
|
||||
@@ -944,7 +944,7 @@ mod tests {
|
||||
]);
|
||||
|
||||
for module_name in existing_modules {
|
||||
let resolved_module = resolve_module(&db, module_name.clone()).unwrap_or_else(|| {
|
||||
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
|
||||
panic!("Expected module {module_name} to exist in the mock stdlib")
|
||||
});
|
||||
let search_path = resolved_module.search_path();
|
||||
@@ -980,7 +980,7 @@ mod tests {
|
||||
let nonexisting_modules = create_module_names(&["importlib", "xml", "xml.etree"]);
|
||||
for module_name in nonexisting_modules {
|
||||
assert!(
|
||||
resolve_module(&db, module_name.clone()).is_none(),
|
||||
resolve_module(&db, &module_name).is_none(),
|
||||
"Unexpectedly resolved a module for {module_name}"
|
||||
);
|
||||
}
|
||||
@@ -1002,11 +1002,11 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let functools_module_name = ModuleName::new_static("functools").unwrap();
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Some(&functools_module),
|
||||
resolve_module(&db, functools_module_name).as_ref()
|
||||
resolve_module(&db, &functools_module_name).as_ref()
|
||||
);
|
||||
assert_eq!(&src, functools_module.search_path());
|
||||
assert_eq!(ModuleKind::Module, functools_module.kind());
|
||||
@@ -1026,7 +1026,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let pydoc_data_topics_name = ModuleName::new_static("pydoc_data.topics").unwrap();
|
||||
let pydoc_data_topics = resolve_module(&db, pydoc_data_topics_name).unwrap();
|
||||
let pydoc_data_topics = resolve_module(&db, &pydoc_data_topics_name).unwrap();
|
||||
|
||||
assert_eq!("pydoc_data.topics", pydoc_data_topics.name());
|
||||
assert_eq!(pydoc_data_topics.search_path(), &stdlib);
|
||||
@@ -1043,7 +1043,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let foo_path = src.join("foo/__init__.py");
|
||||
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
|
||||
assert_eq!("foo", foo_module.name());
|
||||
assert_eq!(&src, foo_module.search_path());
|
||||
@@ -1070,7 +1070,7 @@ mod tests {
|
||||
|
||||
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
|
||||
|
||||
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let foo_init_path = src.join("foo/__init__.py");
|
||||
|
||||
assert_eq!(&src, foo_module.search_path());
|
||||
@@ -1098,11 +1098,11 @@ mod tests {
|
||||
let foo_bar_module_name = ModuleName::new_static("foo.bar").unwrap();
|
||||
|
||||
// `foo.py` takes priority over the `foo` namespace package
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
assert_eq!(foo_module.file().path(&db), &src.join("foo.py"));
|
||||
|
||||
// `foo.bar` isn't recognised as a module
|
||||
let foo_bar_module = resolve_module(&db, foo_bar_module_name.clone());
|
||||
let foo_bar_module = resolve_module(&db, &foo_bar_module_name);
|
||||
assert_eq!(foo_bar_module, None);
|
||||
}
|
||||
|
||||
@@ -1112,7 +1112,7 @@ mod tests {
|
||||
|
||||
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
|
||||
|
||||
let foo = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let foo = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let foo_stub = src.join("foo.pyi");
|
||||
|
||||
assert_eq!(&src, foo.search_path());
|
||||
@@ -1136,7 +1136,7 @@ mod tests {
|
||||
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
|
||||
|
||||
let baz_module =
|
||||
resolve_module(&db, ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
|
||||
resolve_module(&db, &ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
|
||||
let baz_path = src.join("foo/bar/baz.py");
|
||||
|
||||
assert_eq!(&src, baz_module.search_path());
|
||||
@@ -1175,14 +1175,14 @@ mod tests {
|
||||
let one_module_name = ModuleName::new_static("parent.child.one").unwrap();
|
||||
let one_module_path = FilePath::System(src.join("parent/child/one.py"));
|
||||
assert_eq!(
|
||||
resolve_module(&db, one_module_name),
|
||||
resolve_module(&db, &one_module_name),
|
||||
path_to_module(&db, &one_module_path)
|
||||
);
|
||||
|
||||
let two_module_name = ModuleName::new_static("parent.child.two").unwrap();
|
||||
let two_module_path = FilePath::System(site_packages.join("parent/child/two.py"));
|
||||
assert_eq!(
|
||||
resolve_module(&db, two_module_name),
|
||||
resolve_module(&db, &two_module_name),
|
||||
path_to_module(&db, &two_module_path)
|
||||
);
|
||||
}
|
||||
@@ -1215,12 +1215,12 @@ mod tests {
|
||||
|
||||
let one_module_path = FilePath::System(src.join("parent/child/one.py"));
|
||||
let one_module_name =
|
||||
resolve_module(&db, ModuleName::new_static("parent.child.one").unwrap());
|
||||
resolve_module(&db, &ModuleName::new_static("parent.child.one").unwrap());
|
||||
assert_eq!(one_module_name, path_to_module(&db, &one_module_path));
|
||||
|
||||
assert_eq!(
|
||||
None,
|
||||
resolve_module(&db, ModuleName::new_static("parent.child.two").unwrap())
|
||||
resolve_module(&db, &ModuleName::new_static("parent.child.two").unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1236,7 +1236,7 @@ mod tests {
|
||||
.with_site_packages_files(&[("foo.py", "")])
|
||||
.build();
|
||||
|
||||
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let foo_src_path = src.join("foo.py");
|
||||
|
||||
assert_eq!(&src, foo_module.search_path());
|
||||
@@ -1301,8 +1301,8 @@ mod tests {
|
||||
)
|
||||
.context("Invalid program settings")?;
|
||||
|
||||
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let bar_module = resolve_module(&db, ModuleName::new_static("bar").unwrap()).unwrap();
|
||||
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
|
||||
let bar_module = resolve_module(&db, &ModuleName::new_static("bar").unwrap()).unwrap();
|
||||
|
||||
assert_ne!(foo_module, bar_module);
|
||||
|
||||
@@ -1337,7 +1337,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
|
||||
let bar_path = src.join("bar.py");
|
||||
let bar = system_path_to_file(&db, &bar_path).expect("bar.py to exist");
|
||||
@@ -1351,7 +1351,7 @@ mod tests {
|
||||
// Re-query the foo module. The foo module should still be cached because `bar.py` isn't relevant
|
||||
// for resolving `foo`.
|
||||
|
||||
let foo_module2 = resolve_module(&db, foo_module_name);
|
||||
let foo_module2 = resolve_module(&db, &foo_module_name);
|
||||
|
||||
assert!(!db
|
||||
.take_salsa_events()
|
||||
@@ -1368,14 +1368,14 @@ mod tests {
|
||||
let foo_path = src.join("foo.py");
|
||||
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
|
||||
assert_eq!(resolve_module(&db, &foo_module_name), None);
|
||||
|
||||
// Now write the foo file
|
||||
db.write_file(&foo_path, "x = 1")?;
|
||||
|
||||
let foo_file = system_path_to_file(&db, &foo_path).expect("foo.py to exist");
|
||||
|
||||
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
|
||||
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
|
||||
assert_eq!(foo_file, foo_module.file());
|
||||
|
||||
Ok(())
|
||||
@@ -1389,7 +1389,7 @@ mod tests {
|
||||
let TestCase { mut db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
|
||||
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).expect("foo module to exist");
|
||||
let foo_module = resolve_module(&db, &foo_module_name).expect("foo module to exist");
|
||||
let foo_init_path = src.join("foo/__init__.py");
|
||||
|
||||
assert_eq!(&foo_init_path, foo_module.file().path(&db));
|
||||
@@ -1401,7 +1401,7 @@ mod tests {
|
||||
File::sync_path(&mut db, &foo_init_path);
|
||||
File::sync_path(&mut db, foo_init_path.parent().unwrap());
|
||||
|
||||
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
|
||||
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
|
||||
assert_eq!(&src.join("foo.py"), foo_module.file().path(&db));
|
||||
|
||||
Ok(())
|
||||
@@ -1427,7 +1427,7 @@ mod tests {
|
||||
let functools_module_name = ModuleName::new_static("functools").unwrap();
|
||||
let stdlib_functools_path = stdlib.join("functools.pyi");
|
||||
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
assert_eq!(functools_module.search_path(), &stdlib);
|
||||
assert_eq!(
|
||||
Ok(functools_module.file()),
|
||||
@@ -1440,7 +1440,7 @@ mod tests {
|
||||
let site_packages_functools_path = site_packages.join("functools.py");
|
||||
db.write_file(&site_packages_functools_path, "f: int")
|
||||
.unwrap();
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
let events = db.take_salsa_events();
|
||||
assert_function_query_was_not_run(
|
||||
&db,
|
||||
@@ -1473,7 +1473,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let functools_module_name = ModuleName::new_static("functools").unwrap();
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
assert_eq!(functools_module.search_path(), &stdlib);
|
||||
assert_eq!(
|
||||
Ok(functools_module.file()),
|
||||
@@ -1484,7 +1484,7 @@ mod tests {
|
||||
// since first-party files take higher priority in module resolution:
|
||||
let src_functools_path = src.join("functools.py");
|
||||
db.write_file(&src_functools_path, "FOO: int").unwrap();
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
assert_eq!(functools_module.search_path(), &src);
|
||||
assert_eq!(
|
||||
Ok(functools_module.file()),
|
||||
@@ -1515,7 +1515,7 @@ mod tests {
|
||||
let functools_module_name = ModuleName::new_static("functools").unwrap();
|
||||
let src_functools_path = src.join("functools.py");
|
||||
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
assert_eq!(functools_module.search_path(), &src);
|
||||
assert_eq!(
|
||||
Ok(functools_module.file()),
|
||||
@@ -1528,7 +1528,7 @@ mod tests {
|
||||
.remove_file(&src_functools_path)
|
||||
.unwrap();
|
||||
File::sync_path(&mut db, &src_functools_path);
|
||||
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
|
||||
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
|
||||
assert_eq!(functools_module.search_path(), &stdlib);
|
||||
assert_eq!(
|
||||
Ok(functools_module.file()),
|
||||
@@ -1550,8 +1550,8 @@ mod tests {
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let foo_bar_module_name = ModuleName::new_static("foo.bar").unwrap();
|
||||
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let foo_bar_module = resolve_module(&db, foo_bar_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
let foo_bar_module = resolve_module(&db, &foo_bar_module_name).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
foo_module.file().path(&db),
|
||||
@@ -1579,11 +1579,11 @@ mod tests {
|
||||
|
||||
// Lines with leading whitespace in `.pth` files do not parse:
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
assert_eq!(resolve_module(&db, foo_module_name), None);
|
||||
assert_eq!(resolve_module(&db, &foo_module_name), None);
|
||||
|
||||
// Lines with trailing whitespace in `.pth` files do:
|
||||
let bar_module_name = ModuleName::new_static("bar").unwrap();
|
||||
let bar_module = resolve_module(&db, bar_module_name.clone()).unwrap();
|
||||
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
|
||||
assert_eq!(
|
||||
bar_module.file().path(&db),
|
||||
&FilePath::system("/y/src/bar.py")
|
||||
@@ -1602,7 +1602,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
foo_module.file().path(&db),
|
||||
@@ -1650,10 +1650,10 @@ not_a_directory
|
||||
let b_module_name = ModuleName::new_static("b").unwrap();
|
||||
let spam_module_name = ModuleName::new_static("spam").unwrap();
|
||||
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let a_module = resolve_module(&db, a_module_name.clone()).unwrap();
|
||||
let b_module = resolve_module(&db, b_module_name.clone()).unwrap();
|
||||
let spam_module = resolve_module(&db, spam_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
let a_module = resolve_module(&db, &a_module_name).unwrap();
|
||||
let b_module = resolve_module(&db, &b_module_name).unwrap();
|
||||
let spam_module = resolve_module(&db, &spam_module_name).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
foo_module.file().path(&db),
|
||||
@@ -1681,14 +1681,14 @@ not_a_directory
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let bar_module_name = ModuleName::new_static("bar").unwrap();
|
||||
|
||||
let foo_module = resolve_module(&db, foo_module_name).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
assert_eq!(
|
||||
foo_module.file().path(&db),
|
||||
&FilePath::system("/x/src/foo.py")
|
||||
);
|
||||
|
||||
db.clear_salsa_events();
|
||||
let bar_module = resolve_module(&db, bar_module_name).unwrap();
|
||||
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
|
||||
assert_eq!(
|
||||
bar_module.file().path(&db),
|
||||
&FilePath::system("/y/src/bar.py")
|
||||
@@ -1713,7 +1713,7 @@ not_a_directory
|
||||
db.write_files(x_directory).unwrap();
|
||||
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
assert_eq!(
|
||||
foo_module.file().path(&db),
|
||||
&FilePath::system("/x/src/foo.py")
|
||||
@@ -1725,7 +1725,7 @@ not_a_directory
|
||||
|
||||
File::sync_path(&mut db, &site_packages.join("_foo.pth"));
|
||||
|
||||
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
|
||||
assert_eq!(resolve_module(&db, &foo_module_name), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1740,7 +1740,7 @@ not_a_directory
|
||||
db.write_files(x_directory).unwrap();
|
||||
|
||||
let foo_module_name = ModuleName::new_static("foo").unwrap();
|
||||
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
|
||||
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
|
||||
let src_path = SystemPathBuf::from("/x/src");
|
||||
assert_eq!(
|
||||
foo_module.file().path(&db),
|
||||
@@ -1753,7 +1753,7 @@ not_a_directory
|
||||
db.memory_file_system().remove_directory(&src_path).unwrap();
|
||||
File::sync_path(&mut db, &src_path.join("foo.py"));
|
||||
File::sync_path(&mut db, &src_path);
|
||||
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
|
||||
assert_eq!(resolve_module(&db, &foo_module_name), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1812,7 +1812,7 @@ not_a_directory
|
||||
// The editable installs discovered from the `.pth` file in the first `site-packages` directory
|
||||
// take precedence over the second `site-packages` directory...
|
||||
let a_module_name = ModuleName::new_static("a").unwrap();
|
||||
let a_module = resolve_module(&db, a_module_name.clone()).unwrap();
|
||||
let a_module = resolve_module(&db, &a_module_name).unwrap();
|
||||
assert_eq!(a_module.file().path(&db), &editable_install_location);
|
||||
|
||||
db.memory_file_system()
|
||||
@@ -1823,7 +1823,7 @@ not_a_directory
|
||||
// ...But now that the `.pth` file in the first `site-packages` directory has been deleted,
|
||||
// the editable install no longer exists, so the module now resolves to the file in the
|
||||
// second `site-packages` directory
|
||||
let a_module = resolve_module(&db, a_module_name).unwrap();
|
||||
let a_module = resolve_module(&db, &a_module_name).unwrap();
|
||||
assert_eq!(a_module.file().path(&db), &system_site_packages_location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,7 +994,7 @@ class C[T]:
|
||||
let ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: ast::Number::Int(num),
|
||||
..
|
||||
}) = &*assignment.assignment().value
|
||||
}) = assignment.value()
|
||||
else {
|
||||
panic!("should be a number literal")
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use except_handlers::TryNodeContextStackManager;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_db::files::File;
|
||||
@@ -28,22 +29,27 @@ use crate::Db;
|
||||
|
||||
use super::constraint::{Constraint, PatternConstraint};
|
||||
use super::definition::{
|
||||
DefinitionCategory, ExceptHandlerDefinitionNodeRef, MatchPatternDefinitionNodeRef,
|
||||
WithItemDefinitionNodeRef,
|
||||
AssignmentKind, DefinitionCategory, ExceptHandlerDefinitionNodeRef,
|
||||
MatchPatternDefinitionNodeRef, WithItemDefinitionNodeRef,
|
||||
};
|
||||
|
||||
mod except_handlers;
|
||||
|
||||
pub(super) struct SemanticIndexBuilder<'db> {
|
||||
// Builder state
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
module: &'db ParsedModule,
|
||||
scope_stack: Vec<FileScopeId>,
|
||||
/// The assignment we're currently visiting.
|
||||
current_assignment: Option<CurrentAssignment<'db>>,
|
||||
/// The assignments we're currently visiting, with
|
||||
/// the most recent visit at the end of the Vec
|
||||
current_assignments: Vec<CurrentAssignment<'db>>,
|
||||
/// The match case we're currently visiting.
|
||||
current_match_case: Option<CurrentMatchCase<'db>>,
|
||||
/// Flow states at each `break` in the current loop.
|
||||
loop_break_states: Vec<FlowSnapshot>,
|
||||
/// Per-scope contexts regarding nested `try`/`except` statements
|
||||
try_node_context_stack_manager: TryNodeContextStackManager,
|
||||
|
||||
/// Flags about the file's global scope
|
||||
has_future_annotations: bool,
|
||||
@@ -67,9 +73,10 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
file,
|
||||
module: parsed,
|
||||
scope_stack: Vec::new(),
|
||||
current_assignment: None,
|
||||
current_assignments: vec![],
|
||||
current_match_case: None,
|
||||
loop_break_states: vec![],
|
||||
try_node_context_stack_manager: TryNodeContextStackManager::default(),
|
||||
|
||||
has_future_annotations: false,
|
||||
|
||||
@@ -110,6 +117,7 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
kind: node.scope_kind(),
|
||||
descendents: children_start..children_start,
|
||||
};
|
||||
self.try_node_context_stack_manager.enter_nested_scope();
|
||||
|
||||
let file_scope_id = self.scopes.push(scope);
|
||||
self.symbol_tables.push(SymbolTableBuilder::new());
|
||||
@@ -139,6 +147,7 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
let children_end = self.scopes.next_index();
|
||||
let scope = &mut self.scopes[id];
|
||||
scope.descendents = scope.descendents.start..children_end;
|
||||
self.try_node_context_stack_manager.exit_scope();
|
||||
id
|
||||
}
|
||||
|
||||
@@ -227,6 +236,10 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
DefinitionCategory::Binding => use_def.record_binding(symbol, definition),
|
||||
}
|
||||
|
||||
let mut try_node_stack_manager = std::mem::take(&mut self.try_node_context_stack_manager);
|
||||
try_node_stack_manager.record_definition(self);
|
||||
self.try_node_context_stack_manager = try_node_stack_manager;
|
||||
|
||||
definition
|
||||
}
|
||||
|
||||
@@ -238,6 +251,19 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
expression
|
||||
}
|
||||
|
||||
fn push_assignment(&mut self, assignment: CurrentAssignment<'db>) {
|
||||
self.current_assignments.push(assignment);
|
||||
}
|
||||
|
||||
fn pop_assignment(&mut self) {
|
||||
let popped_assignment = self.current_assignments.pop();
|
||||
debug_assert!(popped_assignment.is_some());
|
||||
}
|
||||
|
||||
fn current_assignment(&self) -> Option<&CurrentAssignment<'db>> {
|
||||
self.current_assignments.last()
|
||||
}
|
||||
|
||||
fn add_pattern_constraint(
|
||||
&mut self,
|
||||
subject: &ast::Expr,
|
||||
@@ -359,12 +385,12 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
self.visit_expr(&generator.iter);
|
||||
self.push_scope(scope);
|
||||
|
||||
self.current_assignment = Some(CurrentAssignment::Comprehension {
|
||||
self.push_assignment(CurrentAssignment::Comprehension {
|
||||
node: generator,
|
||||
first: true,
|
||||
});
|
||||
self.visit_expr(&generator.target);
|
||||
self.current_assignment = None;
|
||||
self.pop_assignment();
|
||||
|
||||
for expr in &generator.ifs {
|
||||
self.visit_expr(expr);
|
||||
@@ -374,12 +400,12 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
self.add_standalone_expression(&generator.iter);
|
||||
self.visit_expr(&generator.iter);
|
||||
|
||||
self.current_assignment = Some(CurrentAssignment::Comprehension {
|
||||
self.push_assignment(CurrentAssignment::Comprehension {
|
||||
node: generator,
|
||||
first: false,
|
||||
});
|
||||
self.visit_expr(&generator.target);
|
||||
self.current_assignment = None;
|
||||
self.pop_assignment();
|
||||
|
||||
for expr in &generator.ifs {
|
||||
self.visit_expr(expr);
|
||||
@@ -415,7 +441,7 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
self.pop_scope();
|
||||
assert!(self.scope_stack.is_empty());
|
||||
|
||||
assert!(self.current_assignment.is_none());
|
||||
assert_eq!(&self.current_assignments, &[]);
|
||||
|
||||
let mut symbol_tables: IndexVec<_, _> = self
|
||||
.symbol_tables
|
||||
@@ -563,24 +589,38 @@ where
|
||||
}
|
||||
}
|
||||
ast::Stmt::Assign(node) => {
|
||||
debug_assert!(self.current_assignment.is_none());
|
||||
debug_assert_eq!(&self.current_assignments, &[]);
|
||||
self.visit_expr(&node.value);
|
||||
self.add_standalone_expression(&node.value);
|
||||
self.current_assignment = Some(node.into());
|
||||
for target in &node.targets {
|
||||
for (target_index, target) in node.targets.iter().enumerate() {
|
||||
let kind = match target {
|
||||
ast::Expr::List(_) | ast::Expr::Tuple(_) => Some(AssignmentKind::Sequence),
|
||||
ast::Expr::Name(_) => Some(AssignmentKind::Name),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(kind) = kind {
|
||||
self.push_assignment(CurrentAssignment::Assign {
|
||||
assignment: node,
|
||||
target_index,
|
||||
kind,
|
||||
});
|
||||
}
|
||||
self.visit_expr(target);
|
||||
if kind.is_some() {
|
||||
// only need to pop in the case where we pushed something
|
||||
self.pop_assignment();
|
||||
}
|
||||
}
|
||||
self.current_assignment = None;
|
||||
}
|
||||
ast::Stmt::AnnAssign(node) => {
|
||||
debug_assert!(self.current_assignment.is_none());
|
||||
debug_assert_eq!(&self.current_assignments, &[]);
|
||||
self.visit_expr(&node.annotation);
|
||||
if let Some(value) = &node.value {
|
||||
self.visit_expr(value);
|
||||
}
|
||||
self.current_assignment = Some(node.into());
|
||||
self.push_assignment(node.into());
|
||||
self.visit_expr(&node.target);
|
||||
self.current_assignment = None;
|
||||
self.pop_assignment();
|
||||
}
|
||||
ast::Stmt::AugAssign(
|
||||
aug_assign @ ast::StmtAugAssign {
|
||||
@@ -590,11 +630,11 @@ where
|
||||
value,
|
||||
},
|
||||
) => {
|
||||
debug_assert!(self.current_assignment.is_none());
|
||||
debug_assert_eq!(&self.current_assignments, &[]);
|
||||
self.visit_expr(value);
|
||||
self.current_assignment = Some(aug_assign.into());
|
||||
self.push_assignment(aug_assign.into());
|
||||
self.visit_expr(target);
|
||||
self.current_assignment = None;
|
||||
self.pop_assignment();
|
||||
}
|
||||
ast::Stmt::If(node) => {
|
||||
self.visit_expr(&node.test);
|
||||
@@ -662,9 +702,9 @@ where
|
||||
self.visit_expr(&item.context_expr);
|
||||
if let Some(optional_vars) = item.optional_vars.as_deref() {
|
||||
self.add_standalone_expression(&item.context_expr);
|
||||
self.current_assignment = Some(item.into());
|
||||
self.push_assignment(item.into());
|
||||
self.visit_expr(optional_vars);
|
||||
self.current_assignment = None;
|
||||
self.pop_assignment();
|
||||
}
|
||||
}
|
||||
self.visit_body(body);
|
||||
@@ -689,10 +729,10 @@ where
|
||||
let pre_loop = self.flow_snapshot();
|
||||
let saved_break_states = std::mem::take(&mut self.loop_break_states);
|
||||
|
||||
debug_assert!(self.current_assignment.is_none());
|
||||
self.current_assignment = Some(for_stmt.into());
|
||||
debug_assert_eq!(&self.current_assignments, &[]);
|
||||
self.push_assignment(for_stmt.into());
|
||||
self.visit_expr(target);
|
||||
self.current_assignment = None;
|
||||
self.pop_assignment();
|
||||
|
||||
// TODO: Definitions created by loop variables
|
||||
// (and definitions created inside the body)
|
||||
@@ -753,40 +793,104 @@ where
|
||||
is_star,
|
||||
range: _,
|
||||
}) => {
|
||||
// Save the state prior to visiting any of the `try` block.
|
||||
//
|
||||
// Potentially none of the `try` block could have been executed prior to executing
|
||||
// the `except` block(s) and/or the `finally` block.
|
||||
// We will merge this state with all of the intermediate
|
||||
// states during the `try` block before visiting those suites.
|
||||
let pre_try_block_state = self.flow_snapshot();
|
||||
|
||||
self.try_node_context_stack_manager.push_context();
|
||||
|
||||
// Visit the `try` block!
|
||||
self.visit_body(body);
|
||||
|
||||
for except_handler in handlers {
|
||||
let ast::ExceptHandler::ExceptHandler(except_handler) = except_handler;
|
||||
let ast::ExceptHandlerExceptHandler {
|
||||
name: symbol_name,
|
||||
type_: handled_exceptions,
|
||||
body: handler_body,
|
||||
range: _,
|
||||
} = except_handler;
|
||||
let mut post_except_states = vec![];
|
||||
|
||||
if let Some(handled_exceptions) = handled_exceptions {
|
||||
self.visit_expr(handled_exceptions);
|
||||
// Take a record also of all the intermediate states we encountered
|
||||
// while visiting the `try` block
|
||||
let try_block_snapshots = self.try_node_context_stack_manager.pop_context();
|
||||
|
||||
if !handlers.is_empty() {
|
||||
// Save the state immediately *after* visiting the `try` block
|
||||
// but *before* we prepare for visiting the `except` block(s).
|
||||
//
|
||||
// We will revert to this state prior to visiting the the `else` block,
|
||||
// as there necessarily must have been 0 `except` blocks executed
|
||||
// if we hit the `else` block.
|
||||
let post_try_block_state = self.flow_snapshot();
|
||||
|
||||
// Prepare for visiting the `except` block(s)
|
||||
self.flow_restore(pre_try_block_state);
|
||||
for state in try_block_snapshots {
|
||||
self.flow_merge(state);
|
||||
}
|
||||
|
||||
// If `handled_exceptions` above was `None`, it's something like `except as e:`,
|
||||
// which is invalid syntax. However, it's still pretty obvious here that the user
|
||||
// *wanted* `e` to be bound, so we should still create a definition here nonetheless.
|
||||
if let Some(symbol_name) = symbol_name {
|
||||
let symbol = self.add_symbol(symbol_name.id.clone());
|
||||
let pre_except_state = self.flow_snapshot();
|
||||
let num_handlers = handlers.len();
|
||||
|
||||
self.add_definition(
|
||||
symbol,
|
||||
DefinitionNodeRef::ExceptHandler(ExceptHandlerDefinitionNodeRef {
|
||||
handler: except_handler,
|
||||
is_star: *is_star,
|
||||
}),
|
||||
);
|
||||
for (i, except_handler) in handlers.iter().enumerate() {
|
||||
let ast::ExceptHandler::ExceptHandler(except_handler) = except_handler;
|
||||
let ast::ExceptHandlerExceptHandler {
|
||||
name: symbol_name,
|
||||
type_: handled_exceptions,
|
||||
body: handler_body,
|
||||
range: _,
|
||||
} = except_handler;
|
||||
|
||||
if let Some(handled_exceptions) = handled_exceptions {
|
||||
self.visit_expr(handled_exceptions);
|
||||
}
|
||||
|
||||
// If `handled_exceptions` above was `None`, it's something like `except as e:`,
|
||||
// which is invalid syntax. However, it's still pretty obvious here that the user
|
||||
// *wanted* `e` to be bound, so we should still create a definition here nonetheless.
|
||||
if let Some(symbol_name) = symbol_name {
|
||||
let symbol = self.add_symbol(symbol_name.id.clone());
|
||||
|
||||
self.add_definition(
|
||||
symbol,
|
||||
DefinitionNodeRef::ExceptHandler(ExceptHandlerDefinitionNodeRef {
|
||||
handler: except_handler,
|
||||
is_star: *is_star,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
self.visit_body(handler_body);
|
||||
// Each `except` block is mutually exclusive with all other `except` blocks.
|
||||
post_except_states.push(self.flow_snapshot());
|
||||
|
||||
// It's unnecessary to do the `self.flow_restore()` call for the final except handler,
|
||||
// as we'll immediately call `self.flow_restore()` to a different state
|
||||
// as soon as this loop over the handlers terminates.
|
||||
if i < (num_handlers - 1) {
|
||||
self.flow_restore(pre_except_state.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.visit_body(handler_body);
|
||||
// If we get to the `else` block, we know that 0 of the `except` blocks can have been executed,
|
||||
// and the entire `try` block must have been executed:
|
||||
self.flow_restore(post_try_block_state);
|
||||
}
|
||||
|
||||
self.visit_body(orelse);
|
||||
|
||||
for post_except_state in post_except_states {
|
||||
self.flow_merge(post_except_state);
|
||||
}
|
||||
|
||||
// TODO: there's lots of complexity here that isn't yet handled by our model.
|
||||
// In order to accurately model the semantics of `finally` suites, we in fact need to visit
|
||||
// the suite twice: once under the (current) assumption that either the `try + else` suite
|
||||
// ran to completion or exactly one `except` branch ran to completion, and then again under
|
||||
// the assumption that potentially none of the branches ran to completion and we in fact
|
||||
// jumped from a `try`, `else` or `except` branch straight into the `finally` branch.
|
||||
// This requires rethinking some fundamental assumptions semantic indexing makes.
|
||||
// For more details, see:
|
||||
// - https://astral-sh.notion.site/Exception-handler-control-flow-11348797e1ca80bb8ce1e9aedbbe439d
|
||||
// - https://github.com/astral-sh/ruff/pull/13633#discussion_r1788626702
|
||||
self.visit_body(finalbody);
|
||||
}
|
||||
_ => {
|
||||
@@ -802,7 +906,7 @@ where
|
||||
|
||||
match expr {
|
||||
ast::Expr::Name(name_node @ ast::ExprName { id, ctx, .. }) => {
|
||||
let (is_use, is_definition) = match (ctx, self.current_assignment) {
|
||||
let (is_use, is_definition) = match (ctx, self.current_assignment()) {
|
||||
(ast::ExprContext::Store, Some(CurrentAssignment::AugAssign(_))) => {
|
||||
// For augmented assignment, the target expression is also used.
|
||||
(true, true)
|
||||
@@ -813,14 +917,21 @@ where
|
||||
(ast::ExprContext::Invalid, _) => (false, false),
|
||||
};
|
||||
let symbol = self.add_symbol(id.clone());
|
||||
|
||||
if is_definition {
|
||||
match self.current_assignment {
|
||||
Some(CurrentAssignment::Assign(assignment)) => {
|
||||
match self.current_assignment().copied() {
|
||||
Some(CurrentAssignment::Assign {
|
||||
assignment,
|
||||
target_index,
|
||||
kind,
|
||||
}) => {
|
||||
self.add_definition(
|
||||
symbol,
|
||||
AssignmentDefinitionNodeRef {
|
||||
assignment,
|
||||
target: name_node,
|
||||
target_index,
|
||||
name: name_node,
|
||||
kind,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -879,12 +990,11 @@ where
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
ast::Expr::Named(node) => {
|
||||
debug_assert!(self.current_assignment.is_none());
|
||||
// TODO walrus in comprehensions is implicitly nonlocal
|
||||
self.visit_expr(&node.value);
|
||||
self.current_assignment = Some(node.into());
|
||||
self.push_assignment(node.into());
|
||||
self.visit_expr(&node.target);
|
||||
self.current_assignment = None;
|
||||
self.pop_assignment();
|
||||
}
|
||||
ast::Expr::Lambda(lambda) => {
|
||||
if let Some(parameters) = &lambda.parameters {
|
||||
@@ -1043,9 +1153,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
enum CurrentAssignment<'a> {
|
||||
Assign(&'a ast::StmtAssign),
|
||||
Assign {
|
||||
assignment: &'a ast::StmtAssign,
|
||||
target_index: usize,
|
||||
kind: AssignmentKind,
|
||||
},
|
||||
AnnAssign(&'a ast::StmtAnnAssign),
|
||||
AugAssign(&'a ast::StmtAugAssign),
|
||||
For(&'a ast::StmtFor),
|
||||
@@ -1057,12 +1171,6 @@ enum CurrentAssignment<'a> {
|
||||
WithItem(&'a ast::WithItem),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::StmtAssign> for CurrentAssignment<'a> {
|
||||
fn from(value: &'a ast::StmtAssign) -> Self {
|
||||
Self::Assign(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::StmtAnnAssign> for CurrentAssignment<'a> {
|
||||
fn from(value: &'a ast::StmtAnnAssign) -> Self {
|
||||
Self::AnnAssign(value)
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
use crate::semantic_index::use_def::FlowSnapshot;
|
||||
|
||||
use super::SemanticIndexBuilder;
|
||||
|
||||
/// An abstraction over the fact that each scope should have its own [`TryNodeContextStack`]
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct TryNodeContextStackManager(Vec<TryNodeContextStack>);
|
||||
|
||||
impl TryNodeContextStackManager {
|
||||
/// Push a new [`TryNodeContextStack`] onto the stack of stacks.
|
||||
///
|
||||
/// Each [`TryNodeContextStack`] is only valid for a single scope
|
||||
pub(super) fn enter_nested_scope(&mut self) {
|
||||
self.0.push(TryNodeContextStack::default());
|
||||
}
|
||||
|
||||
/// Pop a new [`TryNodeContextStack`] off the stack of stacks.
|
||||
///
|
||||
/// Each [`TryNodeContextStack`] is only valid for a single scope
|
||||
pub(super) fn exit_scope(&mut self) {
|
||||
let popped_context = self.0.pop();
|
||||
debug_assert!(
|
||||
popped_context.is_some(),
|
||||
"exit_scope() should never be called on an empty stack \
|
||||
(this indicates an unbalanced `enter_nested_scope()`/`exit_scope()` pair of calls)"
|
||||
);
|
||||
}
|
||||
|
||||
/// Push a [`TryNodeContext`] onto the [`TryNodeContextStack`]
|
||||
/// at the top of our stack of stacks
|
||||
pub(super) fn push_context(&mut self) {
|
||||
self.current_try_context_stack().push_context();
|
||||
}
|
||||
|
||||
/// Pop a [`TryNodeContext`] off the [`TryNodeContextStack`]
|
||||
/// at the top of our stack of stacks. Return the Vec of [`FlowSnapshot`]s
|
||||
/// recorded while we were visiting the `try` suite.
|
||||
pub(super) fn pop_context(&mut self) -> Vec<FlowSnapshot> {
|
||||
self.current_try_context_stack().pop_context()
|
||||
}
|
||||
|
||||
/// Retrieve the stack that is at the top of our stack of stacks.
|
||||
/// For each `try` block on that stack, push the snapshot onto the `try` block
|
||||
pub(super) fn record_definition(&mut self, builder: &SemanticIndexBuilder) {
|
||||
self.current_try_context_stack().record_definition(builder);
|
||||
}
|
||||
|
||||
/// Retrieve the [`TryNodeContextStack`] that is relevant for the current scope.
|
||||
fn current_try_context_stack(&mut self) -> &mut TryNodeContextStack {
|
||||
self.0
|
||||
.last_mut()
|
||||
.expect("There should always be at least one `TryBlockContexts` on the stack")
|
||||
}
|
||||
}
|
||||
|
||||
/// The contexts of nested `try`/`except` blocks for a single scope
|
||||
#[derive(Debug, Default)]
|
||||
struct TryNodeContextStack(Vec<TryNodeContext>);
|
||||
|
||||
impl TryNodeContextStack {
|
||||
/// Push a new [`TryNodeContext`] for recording intermediate states
|
||||
/// while visiting a [`ruff_python_ast::StmtTry`] node that has a `finally` branch.
|
||||
fn push_context(&mut self) {
|
||||
self.0.push(TryNodeContext::default());
|
||||
}
|
||||
|
||||
/// Pop a [`TryNodeContext`] off the stack. Return the Vec of [`FlowSnapshot`]s
|
||||
/// recorded while we were visiting the `try` suite.
|
||||
fn pop_context(&mut self) -> Vec<FlowSnapshot> {
|
||||
let TryNodeContext {
|
||||
try_suite_snapshots,
|
||||
} = self
|
||||
.0
|
||||
.pop()
|
||||
.expect("Cannot pop a `try` block off an empty `TryBlockContexts` stack");
|
||||
try_suite_snapshots
|
||||
}
|
||||
|
||||
/// For each `try` block on the stack, push the snapshot onto the `try` block
|
||||
fn record_definition(&mut self, builder: &SemanticIndexBuilder) {
|
||||
for context in &mut self.0 {
|
||||
context.record_definition(builder.flow_snapshot());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context for tracking definitions over the course of a single
|
||||
/// [`ruff_python_ast::StmtTry`] node
|
||||
///
|
||||
/// It will likely be necessary to add more fields to this struct in the future
|
||||
/// when we add more advanced handling of `finally` branches.
|
||||
#[derive(Debug, Default)]
|
||||
struct TryNodeContext {
|
||||
try_suite_snapshots: Vec<FlowSnapshot>,
|
||||
}
|
||||
|
||||
impl TryNodeContext {
|
||||
/// Take a record of what the internal state looked like after a definition
|
||||
fn record_definition(&mut self, snapshot: FlowSnapshot) {
|
||||
self.try_suite_snapshots.push(snapshot);
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,9 @@ pub(crate) struct ImportFromDefinitionNodeRef<'a> {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct AssignmentDefinitionNodeRef<'a> {
|
||||
pub(crate) assignment: &'a ast::StmtAssign,
|
||||
pub(crate) target: &'a ast::ExprName,
|
||||
pub(crate) target_index: usize,
|
||||
pub(crate) name: &'a ast::ExprName,
|
||||
pub(crate) kind: AssignmentKind,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@@ -224,12 +226,17 @@ impl DefinitionNodeRef<'_> {
|
||||
DefinitionNodeRef::NamedExpression(named) => {
|
||||
DefinitionKind::NamedExpression(AstNodeRef::new(parsed, named))
|
||||
}
|
||||
DefinitionNodeRef::Assignment(AssignmentDefinitionNodeRef { assignment, target }) => {
|
||||
DefinitionKind::Assignment(AssignmentDefinitionKind {
|
||||
assignment: AstNodeRef::new(parsed.clone(), assignment),
|
||||
target: AstNodeRef::new(parsed, target),
|
||||
})
|
||||
}
|
||||
DefinitionNodeRef::Assignment(AssignmentDefinitionNodeRef {
|
||||
assignment,
|
||||
target_index,
|
||||
name,
|
||||
kind,
|
||||
}) => DefinitionKind::Assignment(AssignmentDefinitionKind {
|
||||
assignment: AstNodeRef::new(parsed.clone(), assignment),
|
||||
target_index,
|
||||
name: AstNodeRef::new(parsed, name),
|
||||
kind,
|
||||
}),
|
||||
DefinitionNodeRef::AnnotatedAssignment(assign) => {
|
||||
DefinitionKind::AnnotatedAssignment(AstNodeRef::new(parsed, assign))
|
||||
}
|
||||
@@ -300,8 +307,10 @@ impl DefinitionNodeRef<'_> {
|
||||
Self::NamedExpression(node) => node.into(),
|
||||
Self::Assignment(AssignmentDefinitionNodeRef {
|
||||
assignment: _,
|
||||
target,
|
||||
}) => target.into(),
|
||||
target_index: _,
|
||||
name,
|
||||
kind: _,
|
||||
}) => name.into(),
|
||||
Self::AnnotatedAssignment(node) => node.into(),
|
||||
Self::AugmentedAssignment(node) => node.into(),
|
||||
Self::For(ForStmtDefinitionNodeRef {
|
||||
@@ -485,17 +494,34 @@ impl ImportFromDefinitionKind {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AssignmentDefinitionKind {
|
||||
assignment: AstNodeRef<ast::StmtAssign>,
|
||||
target: AstNodeRef<ast::ExprName>,
|
||||
target_index: usize,
|
||||
name: AstNodeRef<ast::ExprName>,
|
||||
kind: AssignmentKind,
|
||||
}
|
||||
|
||||
impl AssignmentDefinitionKind {
|
||||
pub(crate) fn assignment(&self) -> &ast::StmtAssign {
|
||||
self.assignment.node()
|
||||
pub(crate) fn value(&self) -> &ast::Expr {
|
||||
&self.assignment.node().value
|
||||
}
|
||||
|
||||
pub(crate) fn target(&self) -> &ast::ExprName {
|
||||
self.target.node()
|
||||
pub(crate) fn target(&self) -> &ast::Expr {
|
||||
&self.assignment.node().targets[self.target_index]
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> &ast::ExprName {
|
||||
self.name.node()
|
||||
}
|
||||
|
||||
pub(crate) fn kind(&self) -> AssignmentKind {
|
||||
self.kind
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of assignment target expression.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum AssignmentKind {
|
||||
Sequence,
|
||||
Name,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -35,7 +35,7 @@ impl<'db> SemanticModel<'db> {
|
||||
line_index(self.db.upcast(), self.file)
|
||||
}
|
||||
|
||||
pub fn resolve_module(&self, module_name: ModuleName) -> Option<Module> {
|
||||
pub fn resolve_module(&self, module_name: &ModuleName) -> Option<Module> {
|
||||
resolve_module(self.db, module_name)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,15 @@ fn core_module_symbol_ty<'db>(
|
||||
core_module: CoreStdlibModule,
|
||||
symbol: &str,
|
||||
) -> Type<'db> {
|
||||
resolve_module(db, core_module.name())
|
||||
resolve_module(db, &core_module.name())
|
||||
.map(|module| global_symbol_ty(db, module.file(), symbol))
|
||||
.map(|ty| {
|
||||
if ty.is_unbound() {
|
||||
ty
|
||||
} else {
|
||||
ty.replace_unbound_with(db, Type::Never)
|
||||
}
|
||||
})
|
||||
.unwrap_or(Type::Unbound)
|
||||
}
|
||||
|
||||
@@ -76,7 +83,7 @@ pub(crate) fn typing_extensions_symbol_ty<'db>(db: &'db dyn Db, symbol: &str) ->
|
||||
///
|
||||
/// Can return `None` if a custom typeshed is used that is missing the core module in question.
|
||||
fn core_module_scope(db: &dyn Db, core_module: CoreStdlibModule) -> Option<ScopeId<'_>> {
|
||||
resolve_module(db, core_module.name()).map(|module| global_scope(db, module.file()))
|
||||
resolve_module(db, &core_module.name()).map(|module| global_scope(db, module.file()))
|
||||
}
|
||||
|
||||
/// Get the `builtins` module scope.
|
||||
|
||||
@@ -60,7 +60,7 @@ fn symbol_ty_by_id<'db>(db: &'db dyn Db, scope: ScopeId<'db>, symbol: ScopedSymb
|
||||
use_def.public_bindings(symbol),
|
||||
use_def
|
||||
.public_may_be_unbound(symbol)
|
||||
.then_some(Type::Unknown),
|
||||
.then_some(Type::Unbound),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
@@ -79,7 +79,7 @@ fn symbol_ty_by_id<'db>(db: &'db dyn Db, scope: ScopeId<'db>, symbol: ScopedSymb
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `symbol_ty` that takes a symbol name instead of an ID.
|
||||
/// Shorthand for `symbol_ty_by_id` that takes a symbol name instead of an ID.
|
||||
fn symbol_ty<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str) -> Type<'db> {
|
||||
let table = symbol_table(db, scope);
|
||||
table
|
||||
@@ -381,7 +381,7 @@ impl<'db> Type<'db> {
|
||||
Type::Union(union) => {
|
||||
union.map(db, |element| element.replace_unbound_with(db, replacement))
|
||||
}
|
||||
ty => *ty,
|
||||
_ => *self,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,6 +415,11 @@ impl<'db> Type<'db> {
|
||||
(_, Type::Unknown | Type::Any | Type::Todo) => false,
|
||||
(Type::Never, _) => true,
|
||||
(_, Type::Never) => false,
|
||||
(Type::BooleanLiteral(_), Type::Instance(class))
|
||||
if class.is_known(db, KnownClass::Bool) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(Type::IntLiteral(_), Type::Instance(class)) if class.is_known(db, KnownClass::Int) => {
|
||||
true
|
||||
}
|
||||
@@ -444,6 +449,9 @@ impl<'db> Type<'db> {
|
||||
///
|
||||
/// [assignable to]: https://typing.readthedocs.io/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation
|
||||
pub(crate) fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
||||
if self.is_equivalent_to(db, target) {
|
||||
return true;
|
||||
}
|
||||
match (self, target) {
|
||||
(Type::Unknown | Type::Any | Type::Todo, _) => true,
|
||||
(_, Type::Unknown | Type::Any | Type::Todo) => true,
|
||||
@@ -463,6 +471,55 @@ impl<'db> Type<'db> {
|
||||
self == other
|
||||
}
|
||||
|
||||
/// Return true if there is just a single inhabitant for this type.
|
||||
///
|
||||
/// Note: This function aims to have no false positives, but might return `false`
|
||||
/// for more complicated types that are actually singletons.
|
||||
pub(crate) fn is_singleton(self) -> bool {
|
||||
match self {
|
||||
Type::Any
|
||||
| Type::Never
|
||||
| Type::Unknown
|
||||
| Type::Todo
|
||||
| Type::Unbound
|
||||
| Type::Instance(..) // TODO some instance types can be singleton types (EllipsisType, NotImplementedType)
|
||||
| Type::IntLiteral(..)
|
||||
| Type::StringLiteral(..)
|
||||
| Type::BytesLiteral(..)
|
||||
| Type::LiteralString => {
|
||||
// Note: The literal types included in this pattern are not true singletons.
|
||||
// There can be multiple Python objects (at different memory locations) that
|
||||
// are both of type Literal[345], for example.
|
||||
false
|
||||
}
|
||||
Type::None | Type::BooleanLiteral(_) | Type::Function(..) | Type::Class(..) | Type::Module(..) => true,
|
||||
Type::Tuple(..) => {
|
||||
// The empty tuple is a singleton on CPython and PyPy, but not on other Python
|
||||
// implementations such as GraalPy. Its *use* as a singleton is discouraged and
|
||||
// should not be relied on for type narrowing, so we do not treat it as one.
|
||||
// See:
|
||||
// https://docs.python.org/3/reference/expressions.html#parenthesized-forms
|
||||
false
|
||||
}
|
||||
Type::Union(..) => {
|
||||
// A single-element union, where the sole element was a singleton, would itself
|
||||
// be a singleton type. However, unions with length < 2 should never appear in
|
||||
// our model due to [`UnionBuilder::build`].
|
||||
false
|
||||
}
|
||||
Type::Intersection(..) => {
|
||||
// Here, we assume that all intersection types that are singletons would have
|
||||
// been reduced to a different form via [`IntersectionBuilder::build`] by now.
|
||||
// For example:
|
||||
//
|
||||
// bool & ~Literal[False] = Literal[True]
|
||||
// None & (None | int) = None | None & int = None
|
||||
//
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a member access of a type.
|
||||
///
|
||||
/// For example, if `foo` is `Type::Instance(<Bar>)`,
|
||||
@@ -791,14 +848,10 @@ impl<'db> Type<'db> {
|
||||
Type::IntLiteral(number) => Type::StringLiteral(StringLiteralType::new(db, {
|
||||
number.to_string().into_boxed_str()
|
||||
})),
|
||||
Type::BooleanLiteral(true) => {
|
||||
Type::StringLiteral(StringLiteralType::new(db, "True".into()))
|
||||
}
|
||||
Type::BooleanLiteral(false) => {
|
||||
Type::StringLiteral(StringLiteralType::new(db, "False".into()))
|
||||
}
|
||||
Type::BooleanLiteral(true) => Type::StringLiteral(StringLiteralType::new(db, "True")),
|
||||
Type::BooleanLiteral(false) => Type::StringLiteral(StringLiteralType::new(db, "False")),
|
||||
Type::StringLiteral(literal) => Type::StringLiteral(StringLiteralType::new(db, {
|
||||
format!("'{}'", literal.value(db).escape_default()).into()
|
||||
format!("'{}'", literal.value(db).escape_default()).into_boxed_str()
|
||||
})),
|
||||
Type::LiteralString => Type::LiteralString,
|
||||
// TODO: handle more complex types
|
||||
@@ -1381,7 +1434,8 @@ impl<'db> ClassType<'db> {
|
||||
pub fn class_member(self, db: &'db dyn Db, name: &str) -> Type<'db> {
|
||||
let member = self.own_class_member(db, name);
|
||||
if !member.is_unbound() {
|
||||
return member;
|
||||
// TODO diagnostic if maybe unbound?
|
||||
return member.replace_unbound_with(db, Type::Never);
|
||||
}
|
||||
|
||||
self.inherited_class_member(db, name)
|
||||
@@ -1463,6 +1517,12 @@ pub struct StringLiteralType<'db> {
|
||||
value: Box<str>,
|
||||
}
|
||||
|
||||
impl<'db> StringLiteralType<'db> {
|
||||
pub fn len(&self, db: &'db dyn Db) -> usize {
|
||||
self.value(db).len()
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::interned]
|
||||
pub struct BytesLiteralType<'db> {
|
||||
#[return_ref]
|
||||
@@ -1475,6 +1535,16 @@ pub struct TupleType<'db> {
|
||||
elements: Box<[Type<'db>]>,
|
||||
}
|
||||
|
||||
impl<'db> TupleType<'db> {
|
||||
pub fn get(&self, db: &'db dyn Db, index: usize) -> Option<Type<'db>> {
|
||||
self.elements(db).get(index).copied()
|
||||
}
|
||||
|
||||
pub fn len(&self, db: &'db dyn Db) -> usize {
|
||||
self.elements(db).len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
@@ -1514,6 +1584,7 @@ mod tests {
|
||||
enum Ty {
|
||||
Never,
|
||||
Unknown,
|
||||
None,
|
||||
Any,
|
||||
IntLiteral(i64),
|
||||
BoolLiteral(bool),
|
||||
@@ -1530,22 +1601,19 @@ mod tests {
|
||||
match self {
|
||||
Ty::Never => Type::Never,
|
||||
Ty::Unknown => Type::Unknown,
|
||||
Ty::None => Type::None,
|
||||
Ty::Any => Type::Any,
|
||||
Ty::IntLiteral(n) => Type::IntLiteral(n),
|
||||
Ty::StringLiteral(s) => {
|
||||
Type::StringLiteral(StringLiteralType::new(db, (*s).into()))
|
||||
}
|
||||
Ty::StringLiteral(s) => Type::StringLiteral(StringLiteralType::new(db, s)),
|
||||
Ty::BoolLiteral(b) => Type::BooleanLiteral(b),
|
||||
Ty::LiteralString => Type::LiteralString,
|
||||
Ty::BytesLiteral(s) => {
|
||||
Type::BytesLiteral(BytesLiteralType::new(db, s.as_bytes().into()))
|
||||
}
|
||||
Ty::BytesLiteral(s) => Type::BytesLiteral(BytesLiteralType::new(db, s.as_bytes())),
|
||||
Ty::BuiltinInstance(s) => builtins_symbol_ty(db, s).to_instance(db),
|
||||
Ty::Union(tys) => {
|
||||
UnionType::from_elements(db, tys.into_iter().map(|ty| ty.into_type(db)))
|
||||
}
|
||||
Ty::Tuple(tys) => {
|
||||
let elements = tys.into_iter().map(|ty| ty.into_type(db)).collect();
|
||||
let elements: Box<_> = tys.into_iter().map(|ty| ty.into_type(db)).collect();
|
||||
Type::Tuple(TupleType::new(db, elements))
|
||||
}
|
||||
}
|
||||
@@ -1566,6 +1634,7 @@ mod tests {
|
||||
#[test_case(Ty::BytesLiteral("foo"), Ty::BuiltinInstance("bytes"))]
|
||||
#[test_case(Ty::IntLiteral(1), Ty::Union(vec![Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str")]))]
|
||||
#[test_case(Ty::IntLiteral(1), Ty::Union(vec![Ty::Unknown, Ty::BuiltinInstance("str")]))]
|
||||
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))]
|
||||
fn is_assignable_to(from: Ty, to: Ty) {
|
||||
let db = setup_db();
|
||||
assert!(from.into_type(&db).is_assignable_to(&db, to.into_type(&db)));
|
||||
@@ -1618,6 +1687,28 @@ mod tests {
|
||||
assert!(from.into_type(&db).is_equivalent_to(&db, to.into_type(&db)));
|
||||
}
|
||||
|
||||
#[test_case(Ty::None)]
|
||||
#[test_case(Ty::BoolLiteral(true))]
|
||||
#[test_case(Ty::BoolLiteral(false))]
|
||||
fn is_singleton(from: Ty) {
|
||||
let db = setup_db();
|
||||
|
||||
assert!(from.into_type(&db).is_singleton());
|
||||
}
|
||||
|
||||
#[test_case(Ty::Never)]
|
||||
#[test_case(Ty::IntLiteral(345))]
|
||||
#[test_case(Ty::BuiltinInstance("str"))]
|
||||
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))]
|
||||
#[test_case(Ty::Tuple(vec![]))]
|
||||
#[test_case(Ty::Tuple(vec![Ty::None]))]
|
||||
#[test_case(Ty::Tuple(vec![Ty::None, Ty::BoolLiteral(true)]))]
|
||||
fn is_not_singleton(from: Ty) {
|
||||
let db = setup_db();
|
||||
|
||||
assert!(!from.into_type(&db).is_singleton());
|
||||
}
|
||||
|
||||
#[test_case(Ty::IntLiteral(1); "is_int_literal_truthy")]
|
||||
#[test_case(Ty::IntLiteral(-1))]
|
||||
#[test_case(Ty::StringLiteral("foo"))]
|
||||
|
||||
@@ -111,7 +111,7 @@ impl<'db> UnionBuilder<'db> {
|
||||
match self.elements.len() {
|
||||
0 => Type::Never,
|
||||
1 => self.elements[0],
|
||||
_ => Type::Union(UnionType::new(self.db, self.elements.into())),
|
||||
_ => Type::Union(UnionType::new(self.db, self.elements.into_boxed_slice())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,6 +374,12 @@ mod tests {
|
||||
|
||||
let union = UnionType::from_elements(&db, [t0, t1, t2, t3]).expect_union();
|
||||
assert_eq!(union.elements(&db), &[bool_instance_ty, t3]);
|
||||
|
||||
let result_ty = UnionType::from_elements(&db, [bool_instance_ty, t0]);
|
||||
assert_eq!(result_ty, bool_instance_ty);
|
||||
|
||||
let result_ty = UnionType::from_elements(&db, [t0, bool_instance_ty]);
|
||||
assert_eq!(result_ty, bool_instance_ty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -341,12 +341,12 @@ mod tests {
|
||||
Type::Unknown,
|
||||
Type::IntLiteral(-1),
|
||||
global_symbol_ty(&db, mod_file, "A"),
|
||||
Type::StringLiteral(StringLiteralType::new(&db, Box::from("A"))),
|
||||
Type::BytesLiteral(BytesLiteralType::new(&db, Box::from([0]))),
|
||||
Type::BytesLiteral(BytesLiteralType::new(&db, Box::from([7]))),
|
||||
Type::StringLiteral(StringLiteralType::new(&db, "A")),
|
||||
Type::BytesLiteral(BytesLiteralType::new(&db, [0u8].as_slice())),
|
||||
Type::BytesLiteral(BytesLiteralType::new(&db, [7u8].as_slice())),
|
||||
Type::IntLiteral(0),
|
||||
Type::IntLiteral(1),
|
||||
Type::StringLiteral(StringLiteralType::new(&db, Box::from("B"))),
|
||||
Type::StringLiteral(StringLiteralType::new(&db, "B")),
|
||||
global_symbol_ty(&db, mod_file, "foo"),
|
||||
global_symbol_ty(&db, mod_file, "bar"),
|
||||
global_symbol_ty(&db, mod_file, "B"),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -155,13 +155,24 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||
let inference = infer_expression_types(self.db, expression);
|
||||
for (op, comparator) in std::iter::zip(&**ops, &**comparators) {
|
||||
let comp_ty = inference.expression_ty(comparator.scoped_ast_id(self.db, scope));
|
||||
if matches!(op, ast::CmpOp::IsNot) {
|
||||
let ty = IntersectionBuilder::new(self.db)
|
||||
.add_negative(comp_ty)
|
||||
.build();
|
||||
self.constraints.insert(symbol, ty);
|
||||
};
|
||||
// TODO other comparison types
|
||||
match op {
|
||||
ast::CmpOp::IsNot => {
|
||||
if comp_ty.is_singleton() {
|
||||
let ty = IntersectionBuilder::new(self.db)
|
||||
.add_negative(comp_ty)
|
||||
.build();
|
||||
self.constraints.insert(symbol, ty);
|
||||
} else {
|
||||
// Non-singletons cannot be safely narrowed using `is not`
|
||||
}
|
||||
}
|
||||
ast::CmpOp::Is => {
|
||||
self.constraints.insert(symbol, comp_ty);
|
||||
}
|
||||
_ => {
|
||||
// TODO other comparison types
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ static HEADER_RE: Lazy<Regex> =
|
||||
/// Matches a code block fenced by triple backticks, possibly with language and `key=val`
|
||||
/// configuration items following the opening backticks (in the "tag string" of the code block).
|
||||
static CODE_RE: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(r"^```(?<lang>\w+)(?<config>( +\S+)*)\s*\n(?<code>(.|\n)*?)\n```\s*\n").unwrap()
|
||||
Regex::new(r"^```(?<lang>\w+)(?<config>( +\S+)*)\s*\n(?<code>(.|\n)*?)\n?```\s*\n").unwrap()
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -21,7 +21,7 @@ the project the stubs are for, but instead report them here to typeshed.**
|
||||
Further documentation on stub files, typeshed, and Python's typing system in
|
||||
general, can also be found at https://typing.readthedocs.io/en/latest/.
|
||||
|
||||
Typeshed supports Python versions 3.8 and up.
|
||||
Typeshed supports Python versions 3.8 to 3.13.
|
||||
|
||||
## Using
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
91a58b07cdd807b1d965e04ba85af2adab8bf924
|
||||
a871efd90ca2734b3341dde98cffab66f3e08cee
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
__future__: 3.0-
|
||||
__main__: 3.0-
|
||||
_ast: 3.0-
|
||||
_asyncio: 3.0-
|
||||
_bisect: 3.0-
|
||||
_bootlocale: 3.4-3.9
|
||||
_codecs: 3.0-
|
||||
@@ -37,6 +38,7 @@ _imp: 3.0-
|
||||
_interpchannels: 3.13-
|
||||
_interpqueues: 3.13-
|
||||
_interpreters: 3.13-
|
||||
_io: 3.0-
|
||||
_json: 3.0-
|
||||
_locale: 3.0-
|
||||
_lsprof: 3.0-
|
||||
@@ -50,6 +52,8 @@ _pydecimal: 3.5-
|
||||
_random: 3.0-
|
||||
_sitebuiltins: 3.4-
|
||||
_socket: 3.0- # present in 3.0 at runtime, but not in typeshed
|
||||
_sqlite3: 3.0-
|
||||
_ssl: 3.0-
|
||||
_stat: 3.4-
|
||||
_thread: 3.0-
|
||||
_threading_local: 3.0-
|
||||
|
||||
1811
crates/red_knot_vendored/vendor/typeshed/stdlib/_ast.pyi
vendored
1811
crates/red_knot_vendored/vendor/typeshed/stdlib/_ast.pyi
vendored
File diff suppressed because it is too large
Load Diff
120
crates/red_knot_vendored/vendor/typeshed/stdlib/_asyncio.pyi
vendored
Normal file
120
crates/red_knot_vendored/vendor/typeshed/stdlib/_asyncio.pyi
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import sys
|
||||
from asyncio.events import AbstractEventLoop
|
||||
from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable
|
||||
from contextvars import Context
|
||||
from types import FrameType
|
||||
from typing import Any, Literal, TextIO, TypeVar
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from types import GenericAlias
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_T_co = TypeVar("_T_co", covariant=True)
|
||||
_TaskYieldType: TypeAlias = Future[object] | None
|
||||
|
||||
class Future(Awaitable[_T], Iterable[_T]):
|
||||
_state: str
|
||||
@property
|
||||
def _exception(self) -> BaseException | None: ...
|
||||
_blocking: bool
|
||||
@property
|
||||
def _log_traceback(self) -> bool: ...
|
||||
@_log_traceback.setter
|
||||
def _log_traceback(self, val: Literal[False]) -> None: ...
|
||||
_asyncio_future_blocking: bool # is a part of duck-typing contract for `Future`
|
||||
def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ...
|
||||
def __del__(self) -> None: ...
|
||||
def get_loop(self) -> AbstractEventLoop: ...
|
||||
@property
|
||||
def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ...
|
||||
def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def cancel(self, msg: Any | None = None) -> bool: ...
|
||||
else:
|
||||
def cancel(self) -> bool: ...
|
||||
|
||||
def cancelled(self) -> bool: ...
|
||||
def done(self) -> bool: ...
|
||||
def result(self) -> _T: ...
|
||||
def exception(self) -> BaseException | None: ...
|
||||
def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ...
|
||||
def set_result(self, result: _T, /) -> None: ...
|
||||
def set_exception(self, exception: type | BaseException, /) -> None: ...
|
||||
def __iter__(self) -> Generator[Any, None, _T]: ...
|
||||
def __await__(self) -> Generator[Any, None, _T]: ...
|
||||
@property
|
||||
def _loop(self) -> AbstractEventLoop: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
_TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co]
|
||||
elif sys.version_info >= (3, 9):
|
||||
_TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co]
|
||||
else:
|
||||
_TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co]
|
||||
|
||||
# mypy and pyright complain that a subclass of an invariant class shouldn't be covariant.
|
||||
# While this is true in general, here it's sort-of okay to have a covariant subclass,
|
||||
# since the only reason why `asyncio.Future` is invariant is the `set_result()` method,
|
||||
# and `asyncio.Task.set_result()` always raises.
|
||||
class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments]
|
||||
if sys.version_info >= (3, 12):
|
||||
def __init__(
|
||||
self,
|
||||
coro: _TaskCompatibleCoro[_T_co],
|
||||
*,
|
||||
loop: AbstractEventLoop = ...,
|
||||
name: str | None = ...,
|
||||
context: Context | None = None,
|
||||
eager_start: bool = False,
|
||||
) -> None: ...
|
||||
elif sys.version_info >= (3, 11):
|
||||
def __init__(
|
||||
self,
|
||||
coro: _TaskCompatibleCoro[_T_co],
|
||||
*,
|
||||
loop: AbstractEventLoop = ...,
|
||||
name: str | None = ...,
|
||||
context: Context | None = None,
|
||||
) -> None: ...
|
||||
else:
|
||||
def __init__(
|
||||
self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ...
|
||||
) -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
def get_coro(self) -> _TaskCompatibleCoro[_T_co] | None: ...
|
||||
else:
|
||||
def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ...
|
||||
|
||||
def get_name(self) -> str: ...
|
||||
def set_name(self, value: object, /) -> None: ...
|
||||
if sys.version_info >= (3, 12):
|
||||
def get_context(self) -> Context: ...
|
||||
|
||||
def get_stack(self, *, limit: int | None = None) -> list[FrameType]: ...
|
||||
def print_stack(self, *, limit: int | None = None, file: TextIO | None = None) -> None: ...
|
||||
if sys.version_info >= (3, 11):
|
||||
def cancelling(self) -> int: ...
|
||||
def uncancel(self) -> int: ...
|
||||
if sys.version_info < (3, 9):
|
||||
@classmethod
|
||||
def current_task(cls, loop: AbstractEventLoop | None = None) -> Task[Any] | None: ...
|
||||
@classmethod
|
||||
def all_tasks(cls, loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
|
||||
|
||||
def get_event_loop() -> AbstractEventLoop: ...
|
||||
def get_running_loop() -> AbstractEventLoop: ...
|
||||
def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ...
|
||||
def _get_running_loop() -> AbstractEventLoop: ...
|
||||
def _register_task(task: Task[Any]) -> None: ...
|
||||
def _unregister_task(task: Task[Any]) -> None: ...
|
||||
def _enter_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ...
|
||||
def _leave_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ...
|
||||
@@ -20,6 +20,8 @@ _QuotingType: TypeAlias = int
|
||||
|
||||
class Error(Exception): ...
|
||||
|
||||
_DialectLike: TypeAlias = str | Dialect | type[Dialect]
|
||||
|
||||
class Dialect:
|
||||
delimiter: str
|
||||
quotechar: str | None
|
||||
@@ -29,9 +31,18 @@ class Dialect:
|
||||
lineterminator: str
|
||||
quoting: _QuotingType
|
||||
strict: bool
|
||||
def __init__(self) -> None: ...
|
||||
|
||||
_DialectLike: TypeAlias = str | Dialect | type[Dialect]
|
||||
def __init__(
|
||||
self,
|
||||
dialect: _DialectLike | None = ...,
|
||||
delimiter: str = ",",
|
||||
doublequote: bool = True,
|
||||
escapechar: str | None = None,
|
||||
lineterminator: str = "\r\n",
|
||||
quotechar: str | None = '"',
|
||||
quoting: _QuotingType = 0,
|
||||
skipinitialspace: bool = False,
|
||||
strict: bool = False,
|
||||
) -> None: ...
|
||||
|
||||
class _reader(Iterator[list[str]]):
|
||||
@property
|
||||
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
from _typeshed import ReadableBuffer, WriteableBuffer
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
|
||||
from ctypes import CDLL, ArgumentError as ArgumentError
|
||||
from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p
|
||||
from typing import Any, ClassVar, Generic, TypeVar, overload
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
@@ -99,6 +99,9 @@ class _Pointer(_PointerLike, _CData, Generic[_CT]):
|
||||
def __getitem__(self, key: slice, /) -> list[Any]: ...
|
||||
def __setitem__(self, key: int, value: Any, /) -> None: ...
|
||||
|
||||
@overload
|
||||
def POINTER(type: None, /) -> type[c_void_p]: ...
|
||||
@overload
|
||||
def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ...
|
||||
def pointer(obj: _CT, /) -> _Pointer[_CT]: ...
|
||||
|
||||
|
||||
@@ -298,7 +298,7 @@ if sys.version_info >= (3, 9):
|
||||
|
||||
def getmouse() -> tuple[int, int, int, int, int]: ...
|
||||
def getsyx() -> tuple[int, int]: ...
|
||||
def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ...
|
||||
def getwin(file: SupportsRead[bytes], /) -> window: ...
|
||||
def halfdelay(tenths: int, /) -> None: ...
|
||||
def has_colors() -> bool: ...
|
||||
|
||||
@@ -310,7 +310,7 @@ def has_il() -> bool: ...
|
||||
def has_key(key: int, /) -> bool: ...
|
||||
def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ...
|
||||
def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ...
|
||||
def initscr() -> _CursesWindow: ...
|
||||
def initscr() -> window: ...
|
||||
def intrflush(flag: bool, /) -> None: ...
|
||||
def is_term_resized(nlines: int, ncols: int, /) -> bool: ...
|
||||
def isendwin() -> bool: ...
|
||||
@@ -321,8 +321,8 @@ def meta(yes: bool, /) -> None: ...
|
||||
def mouseinterval(interval: int, /) -> None: ...
|
||||
def mousemask(newmask: int, /) -> tuple[int, int]: ...
|
||||
def napms(ms: int, /) -> int: ...
|
||||
def newpad(nlines: int, ncols: int, /) -> _CursesWindow: ...
|
||||
def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> _CursesWindow: ...
|
||||
def newpad(nlines: int, ncols: int, /) -> window: ...
|
||||
def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> window: ...
|
||||
def nl(flag: bool = True, /) -> None: ...
|
||||
def nocbreak() -> None: ...
|
||||
def noecho() -> None: ...
|
||||
@@ -378,7 +378,7 @@ def use_env(flag: bool, /) -> None: ...
|
||||
class error(Exception): ...
|
||||
|
||||
@final
|
||||
class _CursesWindow:
|
||||
class window: # undocumented
|
||||
encoding: str
|
||||
@overload
|
||||
def addch(self, ch: _ChType, attr: int = ...) -> None: ...
|
||||
@@ -431,9 +431,9 @@ class _CursesWindow:
|
||||
def delch(self, y: int, x: int) -> None: ...
|
||||
def deleteln(self) -> None: ...
|
||||
@overload
|
||||
def derwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def derwin(self, begin_y: int, begin_x: int) -> window: ...
|
||||
@overload
|
||||
def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ...
|
||||
def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ...
|
||||
def enclose(self, y: int, x: int, /) -> bool: ...
|
||||
def erase(self) -> None: ...
|
||||
@@ -505,16 +505,16 @@ class _CursesWindow:
|
||||
@overload
|
||||
def noutrefresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ...
|
||||
@overload
|
||||
def overlay(self, destwin: _CursesWindow) -> None: ...
|
||||
def overlay(self, destwin: window) -> None: ...
|
||||
@overload
|
||||
def overlay(
|
||||
self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int
|
||||
self, destwin: window, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int
|
||||
) -> None: ...
|
||||
@overload
|
||||
def overwrite(self, destwin: _CursesWindow) -> None: ...
|
||||
def overwrite(self, destwin: window) -> None: ...
|
||||
@overload
|
||||
def overwrite(
|
||||
self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int
|
||||
self, destwin: window, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int
|
||||
) -> None: ...
|
||||
def putwin(self, file: IO[Any], /) -> None: ...
|
||||
def redrawln(self, beg: int, num: int, /) -> None: ...
|
||||
@@ -530,13 +530,13 @@ class _CursesWindow:
|
||||
def standend(self) -> None: ...
|
||||
def standout(self) -> None: ...
|
||||
@overload
|
||||
def subpad(self, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def subpad(self, begin_y: int, begin_x: int) -> window: ...
|
||||
@overload
|
||||
def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ...
|
||||
@overload
|
||||
def subwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def subwin(self, begin_y: int, begin_x: int) -> window: ...
|
||||
@overload
|
||||
def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ...
|
||||
def syncdown(self) -> None: ...
|
||||
def syncok(self, flag: bool) -> None: ...
|
||||
def syncup(self) -> None: ...
|
||||
@@ -555,4 +555,3 @@ class _ncurses_version(NamedTuple):
|
||||
patch: int
|
||||
|
||||
ncurses_version: _ncurses_version
|
||||
window = _CursesWindow # undocumented
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
import numbers
|
||||
import sys
|
||||
from collections.abc import Container, Sequence
|
||||
from decimal import (
|
||||
Clamped as Clamped,
|
||||
Context as Context,
|
||||
ConversionSyntax as ConversionSyntax,
|
||||
Decimal as Decimal,
|
||||
DecimalException as DecimalException,
|
||||
DecimalTuple as DecimalTuple,
|
||||
DivisionByZero as DivisionByZero,
|
||||
DivisionImpossible as DivisionImpossible,
|
||||
DivisionUndefined as DivisionUndefined,
|
||||
FloatOperation as FloatOperation,
|
||||
Inexact as Inexact,
|
||||
InvalidContext as InvalidContext,
|
||||
InvalidOperation as InvalidOperation,
|
||||
Overflow as Overflow,
|
||||
Rounded as Rounded,
|
||||
Subnormal as Subnormal,
|
||||
Underflow as Underflow,
|
||||
)
|
||||
from types import TracebackType
|
||||
from typing import Any, ClassVar, Final, Literal, NamedTuple, overload
|
||||
from typing_extensions import Self, TypeAlias
|
||||
from typing import Final
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
_Decimal: TypeAlias = Decimal | int
|
||||
_DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int]
|
||||
_ComparableNum: TypeAlias = Decimal | float | numbers.Rational
|
||||
_TrapType: TypeAlias = type[DecimalException]
|
||||
|
||||
class _ContextManager:
|
||||
new_context: Context
|
||||
saved_context: Context
|
||||
def __init__(self, new_context: Context) -> None: ...
|
||||
def __enter__(self) -> Context: ...
|
||||
def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
|
||||
__version__: Final[str]
|
||||
__libmpdec_version__: Final[str]
|
||||
|
||||
class DecimalTuple(NamedTuple):
|
||||
sign: int
|
||||
digits: tuple[int, ...]
|
||||
exponent: int | Literal["n", "N", "F"]
|
||||
|
||||
ROUND_DOWN: Final[str]
|
||||
ROUND_HALF_UP: Final[str]
|
||||
ROUND_HALF_EVEN: Final[str]
|
||||
@@ -32,21 +49,6 @@ MAX_PREC: Final[int]
|
||||
MIN_EMIN: Final[int]
|
||||
MIN_ETINY: Final[int]
|
||||
|
||||
class DecimalException(ArithmeticError): ...
|
||||
class Clamped(DecimalException): ...
|
||||
class InvalidOperation(DecimalException): ...
|
||||
class ConversionSyntax(InvalidOperation): ...
|
||||
class DivisionByZero(DecimalException, ZeroDivisionError): ...
|
||||
class DivisionImpossible(InvalidOperation): ...
|
||||
class DivisionUndefined(InvalidOperation, ZeroDivisionError): ...
|
||||
class Inexact(DecimalException): ...
|
||||
class InvalidContext(InvalidOperation): ...
|
||||
class Rounded(DecimalException): ...
|
||||
class Subnormal(DecimalException): ...
|
||||
class Overflow(Inexact, Rounded): ...
|
||||
class Underflow(Inexact, Rounded, Subnormal): ...
|
||||
class FloatOperation(DecimalException, TypeError): ...
|
||||
|
||||
def setcontext(context: Context, /) -> None: ...
|
||||
def getcontext() -> Context: ...
|
||||
|
||||
@@ -67,215 +69,6 @@ if sys.version_info >= (3, 11):
|
||||
else:
|
||||
def localcontext(ctx: Context | None = None) -> _ContextManager: ...
|
||||
|
||||
class Decimal:
|
||||
def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_float(cls, f: float, /) -> Self: ...
|
||||
def __bool__(self) -> bool: ...
|
||||
def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __hash__(self) -> int: ...
|
||||
def as_tuple(self) -> DecimalTuple: ...
|
||||
def as_integer_ratio(self) -> tuple[int, int]: ...
|
||||
def to_eng_string(self, context: Context | None = None) -> str: ...
|
||||
def __abs__(self) -> Decimal: ...
|
||||
def __add__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ...
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __floordiv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __ge__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __gt__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __le__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __lt__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __mod__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __mul__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __neg__(self) -> Decimal: ...
|
||||
def __pos__(self) -> Decimal: ...
|
||||
def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ...
|
||||
def __radd__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ...
|
||||
def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rmod__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rmul__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rsub__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rtruediv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __sub__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __truediv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __float__(self) -> float: ...
|
||||
def __int__(self) -> int: ...
|
||||
def __trunc__(self) -> int: ...
|
||||
@property
|
||||
def real(self) -> Decimal: ...
|
||||
@property
|
||||
def imag(self) -> Decimal: ...
|
||||
def conjugate(self) -> Decimal: ...
|
||||
def __complex__(self) -> complex: ...
|
||||
@overload
|
||||
def __round__(self) -> int: ...
|
||||
@overload
|
||||
def __round__(self, ndigits: int, /) -> Decimal: ...
|
||||
def __floor__(self) -> int: ...
|
||||
def __ceil__(self) -> int: ...
|
||||
def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ...
|
||||
def normalize(self, context: Context | None = None) -> Decimal: ...
|
||||
def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ...
|
||||
def to_integral_exact(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def to_integral_value(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def to_integral(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def sqrt(self, context: Context | None = None) -> Decimal: ...
|
||||
def max(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def min(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def adjusted(self) -> int: ...
|
||||
def canonical(self) -> Decimal: ...
|
||||
def compare_signal(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def compare_total(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def compare_total_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def copy_abs(self) -> Decimal: ...
|
||||
def copy_negate(self) -> Decimal: ...
|
||||
def copy_sign(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def exp(self, context: Context | None = None) -> Decimal: ...
|
||||
def is_canonical(self) -> bool: ...
|
||||
def is_finite(self) -> bool: ...
|
||||
def is_infinite(self) -> bool: ...
|
||||
def is_nan(self) -> bool: ...
|
||||
def is_normal(self, context: Context | None = None) -> bool: ...
|
||||
def is_qnan(self) -> bool: ...
|
||||
def is_signed(self) -> bool: ...
|
||||
def is_snan(self) -> bool: ...
|
||||
def is_subnormal(self, context: Context | None = None) -> bool: ...
|
||||
def is_zero(self) -> bool: ...
|
||||
def ln(self, context: Context | None = None) -> Decimal: ...
|
||||
def log10(self, context: Context | None = None) -> Decimal: ...
|
||||
def logb(self, context: Context | None = None) -> Decimal: ...
|
||||
def logical_and(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def logical_invert(self, context: Context | None = None) -> Decimal: ...
|
||||
def logical_or(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def logical_xor(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def max_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def min_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def next_minus(self, context: Context | None = None) -> Decimal: ...
|
||||
def next_plus(self, context: Context | None = None) -> Decimal: ...
|
||||
def next_toward(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def number_class(self, context: Context | None = None) -> str: ...
|
||||
def radix(self) -> Decimal: ...
|
||||
def rotate(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def scaleb(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __reduce__(self) -> tuple[type[Self], tuple[str]]: ...
|
||||
def __copy__(self) -> Self: ...
|
||||
def __deepcopy__(self, memo: Any, /) -> Self: ...
|
||||
def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ...
|
||||
|
||||
class _ContextManager:
|
||||
new_context: Context
|
||||
saved_context: Context
|
||||
def __init__(self, new_context: Context) -> None: ...
|
||||
def __enter__(self) -> Context: ...
|
||||
def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
|
||||
_TrapType: TypeAlias = type[DecimalException]
|
||||
|
||||
class Context:
|
||||
# TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime,
|
||||
# even settable attributes like `prec` and `rounding`,
|
||||
# but that's inexpressable in the stub.
|
||||
# Type checkers either ignore it or misinterpret it
|
||||
# if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub
|
||||
prec: int
|
||||
rounding: str
|
||||
Emin: int
|
||||
Emax: int
|
||||
capitals: int
|
||||
clamp: int
|
||||
traps: dict[_TrapType, bool]
|
||||
flags: dict[_TrapType, bool]
|
||||
def __init__(
|
||||
self,
|
||||
prec: int | None = ...,
|
||||
rounding: str | None = ...,
|
||||
Emin: int | None = ...,
|
||||
Emax: int | None = ...,
|
||||
capitals: int | None = ...,
|
||||
clamp: int | None = ...,
|
||||
flags: None | dict[_TrapType, bool] | Container[_TrapType] = ...,
|
||||
traps: None | dict[_TrapType, bool] | Container[_TrapType] = ...,
|
||||
_ignored_flags: list[_TrapType] | None = ...,
|
||||
) -> None: ...
|
||||
def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ...
|
||||
def clear_flags(self) -> None: ...
|
||||
def clear_traps(self) -> None: ...
|
||||
def copy(self) -> Context: ...
|
||||
def __copy__(self) -> Context: ...
|
||||
# see https://github.com/python/cpython/issues/94107
|
||||
__hash__: ClassVar[None] # type: ignore[assignment]
|
||||
def Etiny(self) -> int: ...
|
||||
def Etop(self) -> int: ...
|
||||
def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ...
|
||||
def create_decimal_from_float(self, f: float, /) -> Decimal: ...
|
||||
def abs(self, x: _Decimal, /) -> Decimal: ...
|
||||
def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def canonical(self, x: Decimal, /) -> Decimal: ...
|
||||
def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def copy_abs(self, x: _Decimal, /) -> Decimal: ...
|
||||
def copy_decimal(self, x: _Decimal, /) -> Decimal: ...
|
||||
def copy_negate(self, x: _Decimal, /) -> Decimal: ...
|
||||
def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ...
|
||||
def exp(self, x: _Decimal, /) -> Decimal: ...
|
||||
def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ...
|
||||
def is_canonical(self, x: _Decimal, /) -> bool: ...
|
||||
def is_finite(self, x: _Decimal, /) -> bool: ...
|
||||
def is_infinite(self, x: _Decimal, /) -> bool: ...
|
||||
def is_nan(self, x: _Decimal, /) -> bool: ...
|
||||
def is_normal(self, x: _Decimal, /) -> bool: ...
|
||||
def is_qnan(self, x: _Decimal, /) -> bool: ...
|
||||
def is_signed(self, x: _Decimal, /) -> bool: ...
|
||||
def is_snan(self, x: _Decimal, /) -> bool: ...
|
||||
def is_subnormal(self, x: _Decimal, /) -> bool: ...
|
||||
def is_zero(self, x: _Decimal, /) -> bool: ...
|
||||
def ln(self, x: _Decimal, /) -> Decimal: ...
|
||||
def log10(self, x: _Decimal, /) -> Decimal: ...
|
||||
def logb(self, x: _Decimal, /) -> Decimal: ...
|
||||
def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def logical_invert(self, x: _Decimal, /) -> Decimal: ...
|
||||
def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def minus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def next_minus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def next_plus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def normalize(self, x: _Decimal, /) -> Decimal: ...
|
||||
def number_class(self, x: _Decimal, /) -> str: ...
|
||||
def plus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ...
|
||||
def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def radix(self) -> Decimal: ...
|
||||
def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ...
|
||||
def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def sqrt(self, x: _Decimal, /) -> Decimal: ...
|
||||
def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def to_eng_string(self, x: _Decimal, /) -> str: ...
|
||||
def to_sci_string(self, x: _Decimal, /) -> str: ...
|
||||
def to_integral_exact(self, x: _Decimal, /) -> Decimal: ...
|
||||
def to_integral_value(self, x: _Decimal, /) -> Decimal: ...
|
||||
def to_integral(self, x: _Decimal, /) -> Decimal: ...
|
||||
|
||||
DefaultContext: Context
|
||||
BasicContext: Context
|
||||
ExtendedContext: Context
|
||||
|
||||
195
crates/red_knot_vendored/vendor/typeshed/stdlib/_io.pyi
vendored
Normal file
195
crates/red_knot_vendored/vendor/typeshed/stdlib/_io.pyi
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
import builtins
|
||||
import codecs
|
||||
import sys
|
||||
from _typeshed import FileDescriptorOrPath, MaybeNone, ReadableBuffer, WriteableBuffer
|
||||
from collections.abc import Callable, Iterable, Iterator
|
||||
from io import BufferedIOBase, RawIOBase, TextIOBase, UnsupportedOperation as UnsupportedOperation
|
||||
from os import _Opener
|
||||
from types import TracebackType
|
||||
from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only
|
||||
from typing_extensions import Self
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
DEFAULT_BUFFER_SIZE: Final = 8192
|
||||
|
||||
open = builtins.open
|
||||
|
||||
def open_code(path: str) -> IO[bytes]: ...
|
||||
|
||||
BlockingIOError = builtins.BlockingIOError
|
||||
|
||||
class _IOBase:
|
||||
def __iter__(self) -> Iterator[bytes]: ...
|
||||
def __next__(self) -> bytes: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def fileno(self) -> int: ...
|
||||
def flush(self) -> None: ...
|
||||
def isatty(self) -> bool: ...
|
||||
def readable(self) -> bool: ...
|
||||
read: Callable[..., Any]
|
||||
def readlines(self, hint: int = -1, /) -> list[bytes]: ...
|
||||
def seek(self, offset: int, whence: int = ..., /) -> int: ...
|
||||
def seekable(self) -> bool: ...
|
||||
def tell(self) -> int: ...
|
||||
def truncate(self, size: int | None = ..., /) -> int: ...
|
||||
def writable(self) -> bool: ...
|
||||
write: Callable[..., Any]
|
||||
def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ...
|
||||
def readline(self, size: int | None = -1, /) -> bytes: ...
|
||||
def __del__(self) -> None: ...
|
||||
@property
|
||||
def closed(self) -> bool: ...
|
||||
def _checkClosed(self) -> None: ... # undocumented
|
||||
|
||||
class _RawIOBase(_IOBase):
|
||||
def readall(self) -> bytes: ...
|
||||
# The following methods can return None if the file is in non-blocking mode
|
||||
# and no data is available.
|
||||
def readinto(self, buffer: WriteableBuffer, /) -> int | MaybeNone: ...
|
||||
def write(self, b: ReadableBuffer, /) -> int | MaybeNone: ...
|
||||
def read(self, size: int = -1, /) -> bytes | MaybeNone: ...
|
||||
|
||||
class _BufferedIOBase(_IOBase):
|
||||
def detach(self) -> RawIOBase: ...
|
||||
def readinto(self, buffer: WriteableBuffer, /) -> int: ...
|
||||
def write(self, buffer: ReadableBuffer, /) -> int: ...
|
||||
def readinto1(self, buffer: WriteableBuffer, /) -> int: ...
|
||||
def read(self, size: int | None = ..., /) -> bytes: ...
|
||||
def read1(self, size: int = ..., /) -> bytes: ...
|
||||
|
||||
class FileIO(RawIOBase, _RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes
|
||||
mode: str
|
||||
# The type of "name" equals the argument passed in to the constructor,
|
||||
# but that can make FileIO incompatible with other I/O types that assume
|
||||
# "name" is a str. In the future, making FileIO generic might help.
|
||||
name: Any
|
||||
def __init__(
|
||||
self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ...
|
||||
) -> None: ...
|
||||
@property
|
||||
def closefd(self) -> bool: ...
|
||||
|
||||
class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes
|
||||
def __init__(self, initial_bytes: ReadableBuffer = ...) -> None: ...
|
||||
# BytesIO does not contain a "name" field. This workaround is necessary
|
||||
# to allow BytesIO sub-classes to add this field, as it is defined
|
||||
# as a read-only property on IO[].
|
||||
name: Any
|
||||
def getvalue(self) -> bytes: ...
|
||||
def getbuffer(self) -> memoryview: ...
|
||||
def read1(self, size: int | None = -1, /) -> bytes: ...
|
||||
|
||||
class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes
|
||||
raw: RawIOBase
|
||||
def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ...
|
||||
def peek(self, size: int = 0, /) -> bytes: ...
|
||||
|
||||
class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes
|
||||
raw: RawIOBase
|
||||
def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ...
|
||||
def write(self, buffer: ReadableBuffer, /) -> int: ...
|
||||
|
||||
class BufferedRandom(BufferedReader, BufferedWriter, BufferedIOBase, _BufferedIOBase): # type: ignore[misc] # incompatible definitions of methods in the base classes
|
||||
def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this
|
||||
|
||||
class BufferedRWPair(BufferedIOBase, _BufferedIOBase):
|
||||
def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = ...) -> None: ...
|
||||
def peek(self, size: int = ..., /) -> bytes: ...
|
||||
|
||||
class _TextIOBase(_IOBase):
|
||||
encoding: str
|
||||
errors: str | None
|
||||
newlines: str | tuple[str, ...] | None
|
||||
def __iter__(self) -> Iterator[str]: ... # type: ignore[override]
|
||||
def __next__(self) -> str: ... # type: ignore[override]
|
||||
def detach(self) -> BinaryIO: ...
|
||||
def write(self, s: str, /) -> int: ...
|
||||
def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override]
|
||||
def readline(self, size: int = ..., /) -> str: ... # type: ignore[override]
|
||||
def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override]
|
||||
def read(self, size: int | None = ..., /) -> str: ...
|
||||
|
||||
@type_check_only
|
||||
class _WrappedBuffer(Protocol):
|
||||
# "name" is wrapped by TextIOWrapper. Its type is inconsistent between
|
||||
# the various I/O types, see the comments on TextIOWrapper.name and
|
||||
# TextIO.name.
|
||||
@property
|
||||
def name(self) -> Any: ...
|
||||
@property
|
||||
def closed(self) -> bool: ...
|
||||
def read(self, size: int = ..., /) -> ReadableBuffer: ...
|
||||
# Optional: def read1(self, size: int, /) -> ReadableBuffer: ...
|
||||
def write(self, b: bytes, /) -> object: ...
|
||||
def flush(self) -> object: ...
|
||||
def close(self) -> object: ...
|
||||
def seekable(self) -> bool: ...
|
||||
def readable(self) -> bool: ...
|
||||
def writable(self) -> bool: ...
|
||||
def truncate(self, size: int, /) -> int: ...
|
||||
def fileno(self) -> int: ...
|
||||
def isatty(self) -> bool: ...
|
||||
# Optional: Only needs to be present if seekable() returns True.
|
||||
# def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ...
|
||||
# def tell(self) -> int: ...
|
||||
|
||||
_BufferT_co = TypeVar("_BufferT_co", bound=_WrappedBuffer, default=_WrappedBuffer, covariant=True)
|
||||
|
||||
class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]): # type: ignore[misc] # incompatible definitions of write in the base classes
|
||||
def __init__(
|
||||
self,
|
||||
buffer: _BufferT_co,
|
||||
encoding: str | None = None,
|
||||
errors: str | None = None,
|
||||
newline: str | None = None,
|
||||
line_buffering: bool = False,
|
||||
write_through: bool = False,
|
||||
) -> None: ...
|
||||
# Equals the "buffer" argument passed in to the constructor.
|
||||
@property
|
||||
def buffer(self) -> _BufferT_co: ... # type: ignore[override]
|
||||
@property
|
||||
def line_buffering(self) -> bool: ...
|
||||
@property
|
||||
def write_through(self) -> bool: ...
|
||||
def reconfigure(
|
||||
self,
|
||||
*,
|
||||
encoding: str | None = None,
|
||||
errors: str | None = None,
|
||||
newline: str | None = None,
|
||||
line_buffering: bool | None = None,
|
||||
write_through: bool | None = None,
|
||||
) -> None: ...
|
||||
def readline(self, size: int = -1, /) -> str: ... # type: ignore[override]
|
||||
# Equals the "buffer" argument passed in to the constructor.
|
||||
def detach(self) -> _BufferT_co: ... # type: ignore[override]
|
||||
# TextIOWrapper's version of seek only supports a limited subset of
|
||||
# operations.
|
||||
def seek(self, cookie: int, whence: int = 0, /) -> int: ...
|
||||
|
||||
class StringIO(TextIOWrapper, TextIOBase, _TextIOBase): # type: ignore[misc] # incompatible definitions of write in the base classes
|
||||
def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ...
|
||||
# StringIO does not contain a "name" field. This workaround is necessary
|
||||
# to allow StringIO sub-classes to add this field, as it is defined
|
||||
# as a read-only property on IO[].
|
||||
name: Any
|
||||
def getvalue(self) -> str: ...
|
||||
|
||||
class IncrementalNewlineDecoder(codecs.IncrementalDecoder):
|
||||
def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = ...) -> None: ...
|
||||
def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ...
|
||||
@property
|
||||
def newlines(self) -> str | tuple[str, ...] | None: ...
|
||||
def setstate(self, state: tuple[bytes, int], /) -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
@overload
|
||||
def text_encoding(encoding: None, stacklevel: int = 2, /) -> Literal["locale", "utf-8"]: ...
|
||||
@overload
|
||||
def text_encoding(encoding: _T, stacklevel: int = 2, /) -> _T: ...
|
||||
@@ -1,18 +1,16 @@
|
||||
import sys
|
||||
from _typeshed import SupportsGetItem
|
||||
from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence
|
||||
from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, SupportsIndex, TypeVar, final, overload
|
||||
from typing_extensions import ParamSpec, TypeAlias, TypeIs, TypeVarTuple, Unpack
|
||||
from operator import attrgetter as attrgetter, itemgetter as itemgetter, methodcaller as methodcaller
|
||||
from typing import Any, AnyStr, Protocol, SupportsAbs, SupportsIndex, TypeVar, overload
|
||||
from typing_extensions import ParamSpec, TypeAlias, TypeIs
|
||||
|
||||
_R = TypeVar("_R")
|
||||
_T = TypeVar("_T")
|
||||
_T_co = TypeVar("_T_co", covariant=True)
|
||||
_T1 = TypeVar("_T1")
|
||||
_T2 = TypeVar("_T2")
|
||||
_K = TypeVar("_K")
|
||||
_V = TypeVar("_V")
|
||||
_P = ParamSpec("_P")
|
||||
_Ts = TypeVarTuple("_Ts")
|
||||
|
||||
# The following protocols return "Any" instead of bool, since the comparison
|
||||
# operators can be overloaded to return an arbitrary object. For example,
|
||||
@@ -92,40 +90,6 @@ def setitem(a: MutableSequence[_T], b: slice, c: Sequence[_T], /) -> None: ...
|
||||
@overload
|
||||
def setitem(a: MutableMapping[_K, _V], b: _K, c: _V, /) -> None: ...
|
||||
def length_hint(obj: object, default: int = 0, /) -> int: ...
|
||||
@final
|
||||
class attrgetter(Generic[_T_co]):
|
||||
@overload
|
||||
def __new__(cls, attr: str, /) -> attrgetter[Any]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, attr2: str, /) -> attrgetter[tuple[Any, Any]]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, attr2: str, attr3: str, /) -> attrgetter[tuple[Any, Any, Any]]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, attr2: str, attr3: str, attr4: str, /) -> attrgetter[tuple[Any, Any, Any, Any]]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, /, *attrs: str) -> attrgetter[tuple[Any, ...]]: ...
|
||||
def __call__(self, obj: Any, /) -> _T_co: ...
|
||||
|
||||
@final
|
||||
class itemgetter(Generic[_T_co]):
|
||||
@overload
|
||||
def __new__(cls, item: _T, /) -> itemgetter[_T]: ...
|
||||
@overload
|
||||
def __new__(cls, item1: _T1, item2: _T2, /, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ...
|
||||
# __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie:
|
||||
# TypeVar "_KT_contra@SupportsGetItem" is contravariant
|
||||
# "tuple[int, int]" is incompatible with protocol "SupportsIndex"
|
||||
# preventing [_T_co, ...] instead of [Any, ...]
|
||||
#
|
||||
# A suspected mypy issue prevents using [..., _T] instead of [..., Any] here.
|
||||
# https://github.com/python/mypy/issues/14032
|
||||
def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ...
|
||||
|
||||
@final
|
||||
class methodcaller:
|
||||
def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ...
|
||||
def __call__(self, obj: Any) -> Any: ...
|
||||
|
||||
def iadd(a: Any, b: Any, /) -> Any: ...
|
||||
def iand(a: Any, b: Any, /) -> Any: ...
|
||||
def iconcat(a: Any, b: Any, /) -> Any: ...
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer, WriteableBuffer
|
||||
from collections.abc import Iterable
|
||||
from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout
|
||||
from typing import Any, SupportsIndex, overload
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
@@ -666,18 +667,6 @@ if sys.platform != "win32":
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IPX_TYPE: int
|
||||
|
||||
# ===== Exceptions =====
|
||||
|
||||
error = OSError
|
||||
|
||||
class herror(error): ...
|
||||
class gaierror(error): ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
timeout = TimeoutError
|
||||
else:
|
||||
class timeout(error): ...
|
||||
|
||||
# ===== Classes =====
|
||||
|
||||
class socket:
|
||||
@@ -687,8 +676,9 @@ class socket:
|
||||
def type(self) -> int: ...
|
||||
@property
|
||||
def proto(self) -> int: ...
|
||||
# F811: "Redefinition of unused `timeout`"
|
||||
@property
|
||||
def timeout(self) -> float | None: ...
|
||||
def timeout(self) -> float | None: ... # noqa: F811
|
||||
if sys.platform == "win32":
|
||||
def __init__(
|
||||
self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = ...
|
||||
@@ -788,7 +778,9 @@ def inet_ntoa(packed_ip: ReadableBuffer, /) -> str: ...
|
||||
def inet_pton(address_family: int, ip_string: str, /) -> bytes: ...
|
||||
def inet_ntop(address_family: int, packed_ip: ReadableBuffer, /) -> str: ...
|
||||
def getdefaulttimeout() -> float | None: ...
|
||||
def setdefaulttimeout(timeout: float | None, /) -> None: ...
|
||||
|
||||
# F811: "Redefinition of unused `timeout`"
|
||||
def setdefaulttimeout(timeout: float | None, /) -> None: ... # noqa: F811
|
||||
|
||||
if sys.platform != "win32":
|
||||
def sethostname(name: str, /) -> None: ...
|
||||
|
||||
312
crates/red_knot_vendored/vendor/typeshed/stdlib/_sqlite3.pyi
vendored
Normal file
312
crates/red_knot_vendored/vendor/typeshed/stdlib/_sqlite3.pyi
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer, StrOrBytesPath
|
||||
from collections.abc import Callable
|
||||
from sqlite3 import (
|
||||
Connection as Connection,
|
||||
Cursor as Cursor,
|
||||
DatabaseError as DatabaseError,
|
||||
DataError as DataError,
|
||||
Error as Error,
|
||||
IntegrityError as IntegrityError,
|
||||
InterfaceError as InterfaceError,
|
||||
InternalError as InternalError,
|
||||
NotSupportedError as NotSupportedError,
|
||||
OperationalError as OperationalError,
|
||||
PrepareProtocol as PrepareProtocol,
|
||||
ProgrammingError as ProgrammingError,
|
||||
Row as Row,
|
||||
Warning as Warning,
|
||||
)
|
||||
from typing import Any, Final, Literal, TypeVar, overload
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from sqlite3 import Blob as Blob
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_ConnectionT = TypeVar("_ConnectionT", bound=Connection)
|
||||
_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None
|
||||
_Adapter: TypeAlias = Callable[[_T], _SqliteData]
|
||||
_Converter: TypeAlias = Callable[[bytes], Any]
|
||||
|
||||
PARSE_COLNAMES: Final[int]
|
||||
PARSE_DECLTYPES: Final[int]
|
||||
SQLITE_ALTER_TABLE: Final[int]
|
||||
SQLITE_ANALYZE: Final[int]
|
||||
SQLITE_ATTACH: Final[int]
|
||||
SQLITE_CREATE_INDEX: Final[int]
|
||||
SQLITE_CREATE_TABLE: Final[int]
|
||||
SQLITE_CREATE_TEMP_INDEX: Final[int]
|
||||
SQLITE_CREATE_TEMP_TABLE: Final[int]
|
||||
SQLITE_CREATE_TEMP_TRIGGER: Final[int]
|
||||
SQLITE_CREATE_TEMP_VIEW: Final[int]
|
||||
SQLITE_CREATE_TRIGGER: Final[int]
|
||||
SQLITE_CREATE_VIEW: Final[int]
|
||||
SQLITE_CREATE_VTABLE: Final[int]
|
||||
SQLITE_DELETE: Final[int]
|
||||
SQLITE_DENY: Final[int]
|
||||
SQLITE_DETACH: Final[int]
|
||||
SQLITE_DONE: Final[int]
|
||||
SQLITE_DROP_INDEX: Final[int]
|
||||
SQLITE_DROP_TABLE: Final[int]
|
||||
SQLITE_DROP_TEMP_INDEX: Final[int]
|
||||
SQLITE_DROP_TEMP_TABLE: Final[int]
|
||||
SQLITE_DROP_TEMP_TRIGGER: Final[int]
|
||||
SQLITE_DROP_TEMP_VIEW: Final[int]
|
||||
SQLITE_DROP_TRIGGER: Final[int]
|
||||
SQLITE_DROP_VIEW: Final[int]
|
||||
SQLITE_DROP_VTABLE: Final[int]
|
||||
SQLITE_FUNCTION: Final[int]
|
||||
SQLITE_IGNORE: Final[int]
|
||||
SQLITE_INSERT: Final[int]
|
||||
SQLITE_OK: Final[int]
|
||||
SQLITE_PRAGMA: Final[int]
|
||||
SQLITE_READ: Final[int]
|
||||
SQLITE_RECURSIVE: Final[int]
|
||||
SQLITE_REINDEX: Final[int]
|
||||
SQLITE_SAVEPOINT: Final[int]
|
||||
SQLITE_SELECT: Final[int]
|
||||
SQLITE_TRANSACTION: Final[int]
|
||||
SQLITE_UPDATE: Final[int]
|
||||
adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]]
|
||||
converters: dict[str, _Converter]
|
||||
sqlite_version: str
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
version: str
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
LEGACY_TRANSACTION_CONTROL: Final[int]
|
||||
SQLITE_DBCONFIG_DEFENSIVE: Final[int]
|
||||
SQLITE_DBCONFIG_DQS_DDL: Final[int]
|
||||
SQLITE_DBCONFIG_DQS_DML: Final[int]
|
||||
SQLITE_DBCONFIG_ENABLE_FKEY: Final[int]
|
||||
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final[int]
|
||||
SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final[int]
|
||||
SQLITE_DBCONFIG_ENABLE_QPSG: Final[int]
|
||||
SQLITE_DBCONFIG_ENABLE_TRIGGER: Final[int]
|
||||
SQLITE_DBCONFIG_ENABLE_VIEW: Final[int]
|
||||
SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final[int]
|
||||
SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final[int]
|
||||
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final[int]
|
||||
SQLITE_DBCONFIG_RESET_DATABASE: Final[int]
|
||||
SQLITE_DBCONFIG_TRIGGER_EQP: Final[int]
|
||||
SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final[int]
|
||||
SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final[int]
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
SQLITE_ABORT: Final[int]
|
||||
SQLITE_ABORT_ROLLBACK: Final[int]
|
||||
SQLITE_AUTH: Final[int]
|
||||
SQLITE_AUTH_USER: Final[int]
|
||||
SQLITE_BUSY: Final[int]
|
||||
SQLITE_BUSY_RECOVERY: Final[int]
|
||||
SQLITE_BUSY_SNAPSHOT: Final[int]
|
||||
SQLITE_BUSY_TIMEOUT: Final[int]
|
||||
SQLITE_CANTOPEN: Final[int]
|
||||
SQLITE_CANTOPEN_CONVPATH: Final[int]
|
||||
SQLITE_CANTOPEN_DIRTYWAL: Final[int]
|
||||
SQLITE_CANTOPEN_FULLPATH: Final[int]
|
||||
SQLITE_CANTOPEN_ISDIR: Final[int]
|
||||
SQLITE_CANTOPEN_NOTEMPDIR: Final[int]
|
||||
SQLITE_CANTOPEN_SYMLINK: Final[int]
|
||||
SQLITE_CONSTRAINT: Final[int]
|
||||
SQLITE_CONSTRAINT_CHECK: Final[int]
|
||||
SQLITE_CONSTRAINT_COMMITHOOK: Final[int]
|
||||
SQLITE_CONSTRAINT_FOREIGNKEY: Final[int]
|
||||
SQLITE_CONSTRAINT_FUNCTION: Final[int]
|
||||
SQLITE_CONSTRAINT_NOTNULL: Final[int]
|
||||
SQLITE_CONSTRAINT_PINNED: Final[int]
|
||||
SQLITE_CONSTRAINT_PRIMARYKEY: Final[int]
|
||||
SQLITE_CONSTRAINT_ROWID: Final[int]
|
||||
SQLITE_CONSTRAINT_TRIGGER: Final[int]
|
||||
SQLITE_CONSTRAINT_UNIQUE: Final[int]
|
||||
SQLITE_CONSTRAINT_VTAB: Final[int]
|
||||
SQLITE_CORRUPT: Final[int]
|
||||
SQLITE_CORRUPT_INDEX: Final[int]
|
||||
SQLITE_CORRUPT_SEQUENCE: Final[int]
|
||||
SQLITE_CORRUPT_VTAB: Final[int]
|
||||
SQLITE_EMPTY: Final[int]
|
||||
SQLITE_ERROR: Final[int]
|
||||
SQLITE_ERROR_MISSING_COLLSEQ: Final[int]
|
||||
SQLITE_ERROR_RETRY: Final[int]
|
||||
SQLITE_ERROR_SNAPSHOT: Final[int]
|
||||
SQLITE_FORMAT: Final[int]
|
||||
SQLITE_FULL: Final[int]
|
||||
SQLITE_INTERNAL: Final[int]
|
||||
SQLITE_INTERRUPT: Final[int]
|
||||
SQLITE_IOERR: Final[int]
|
||||
SQLITE_IOERR_ACCESS: Final[int]
|
||||
SQLITE_IOERR_AUTH: Final[int]
|
||||
SQLITE_IOERR_BEGIN_ATOMIC: Final[int]
|
||||
SQLITE_IOERR_BLOCKED: Final[int]
|
||||
SQLITE_IOERR_CHECKRESERVEDLOCK: Final[int]
|
||||
SQLITE_IOERR_CLOSE: Final[int]
|
||||
SQLITE_IOERR_COMMIT_ATOMIC: Final[int]
|
||||
SQLITE_IOERR_CONVPATH: Final[int]
|
||||
SQLITE_IOERR_CORRUPTFS: Final[int]
|
||||
SQLITE_IOERR_DATA: Final[int]
|
||||
SQLITE_IOERR_DELETE: Final[int]
|
||||
SQLITE_IOERR_DELETE_NOENT: Final[int]
|
||||
SQLITE_IOERR_DIR_CLOSE: Final[int]
|
||||
SQLITE_IOERR_DIR_FSYNC: Final[int]
|
||||
SQLITE_IOERR_FSTAT: Final[int]
|
||||
SQLITE_IOERR_FSYNC: Final[int]
|
||||
SQLITE_IOERR_GETTEMPPATH: Final[int]
|
||||
SQLITE_IOERR_LOCK: Final[int]
|
||||
SQLITE_IOERR_MMAP: Final[int]
|
||||
SQLITE_IOERR_NOMEM: Final[int]
|
||||
SQLITE_IOERR_RDLOCK: Final[int]
|
||||
SQLITE_IOERR_READ: Final[int]
|
||||
SQLITE_IOERR_ROLLBACK_ATOMIC: Final[int]
|
||||
SQLITE_IOERR_SEEK: Final[int]
|
||||
SQLITE_IOERR_SHMLOCK: Final[int]
|
||||
SQLITE_IOERR_SHMMAP: Final[int]
|
||||
SQLITE_IOERR_SHMOPEN: Final[int]
|
||||
SQLITE_IOERR_SHMSIZE: Final[int]
|
||||
SQLITE_IOERR_SHORT_READ: Final[int]
|
||||
SQLITE_IOERR_TRUNCATE: Final[int]
|
||||
SQLITE_IOERR_UNLOCK: Final[int]
|
||||
SQLITE_IOERR_VNODE: Final[int]
|
||||
SQLITE_IOERR_WRITE: Final[int]
|
||||
SQLITE_LIMIT_ATTACHED: Final[int]
|
||||
SQLITE_LIMIT_COLUMN: Final[int]
|
||||
SQLITE_LIMIT_COMPOUND_SELECT: Final[int]
|
||||
SQLITE_LIMIT_EXPR_DEPTH: Final[int]
|
||||
SQLITE_LIMIT_FUNCTION_ARG: Final[int]
|
||||
SQLITE_LIMIT_LENGTH: Final[int]
|
||||
SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final[int]
|
||||
SQLITE_LIMIT_SQL_LENGTH: Final[int]
|
||||
SQLITE_LIMIT_TRIGGER_DEPTH: Final[int]
|
||||
SQLITE_LIMIT_VARIABLE_NUMBER: Final[int]
|
||||
SQLITE_LIMIT_VDBE_OP: Final[int]
|
||||
SQLITE_LIMIT_WORKER_THREADS: Final[int]
|
||||
SQLITE_LOCKED: Final[int]
|
||||
SQLITE_LOCKED_SHAREDCACHE: Final[int]
|
||||
SQLITE_LOCKED_VTAB: Final[int]
|
||||
SQLITE_MISMATCH: Final[int]
|
||||
SQLITE_MISUSE: Final[int]
|
||||
SQLITE_NOLFS: Final[int]
|
||||
SQLITE_NOMEM: Final[int]
|
||||
SQLITE_NOTADB: Final[int]
|
||||
SQLITE_NOTFOUND: Final[int]
|
||||
SQLITE_NOTICE: Final[int]
|
||||
SQLITE_NOTICE_RECOVER_ROLLBACK: Final[int]
|
||||
SQLITE_NOTICE_RECOVER_WAL: Final[int]
|
||||
SQLITE_OK_LOAD_PERMANENTLY: Final[int]
|
||||
SQLITE_OK_SYMLINK: Final[int]
|
||||
SQLITE_PERM: Final[int]
|
||||
SQLITE_PROTOCOL: Final[int]
|
||||
SQLITE_RANGE: Final[int]
|
||||
SQLITE_READONLY: Final[int]
|
||||
SQLITE_READONLY_CANTINIT: Final[int]
|
||||
SQLITE_READONLY_CANTLOCK: Final[int]
|
||||
SQLITE_READONLY_DBMOVED: Final[int]
|
||||
SQLITE_READONLY_DIRECTORY: Final[int]
|
||||
SQLITE_READONLY_RECOVERY: Final[int]
|
||||
SQLITE_READONLY_ROLLBACK: Final[int]
|
||||
SQLITE_ROW: Final[int]
|
||||
SQLITE_SCHEMA: Final[int]
|
||||
SQLITE_TOOBIG: Final[int]
|
||||
SQLITE_WARNING: Final[int]
|
||||
SQLITE_WARNING_AUTOINDEX: Final[int]
|
||||
threadsafety: Final[int]
|
||||
|
||||
# Can take or return anything depending on what's in the registry.
|
||||
@overload
|
||||
def adapt(obj: Any, proto: Any, /) -> Any: ...
|
||||
@overload
|
||||
def adapt(obj: Any, proto: Any, alt: _T, /) -> Any | _T: ...
|
||||
def complete_statement(statement: str) -> bool: ...
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
@overload
|
||||
def connect(
|
||||
database: StrOrBytesPath,
|
||||
timeout: float = 5.0,
|
||||
detect_types: int = 0,
|
||||
isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
|
||||
check_same_thread: bool = True,
|
||||
cached_statements: int = 128,
|
||||
uri: bool = False,
|
||||
*,
|
||||
autocommit: bool = ...,
|
||||
) -> Connection: ...
|
||||
@overload
|
||||
def connect(
|
||||
database: StrOrBytesPath,
|
||||
timeout: float,
|
||||
detect_types: int,
|
||||
isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None,
|
||||
check_same_thread: bool,
|
||||
factory: type[_ConnectionT],
|
||||
cached_statements: int = 128,
|
||||
uri: bool = False,
|
||||
*,
|
||||
autocommit: bool = ...,
|
||||
) -> _ConnectionT: ...
|
||||
@overload
|
||||
def connect(
|
||||
database: StrOrBytesPath,
|
||||
timeout: float = 5.0,
|
||||
detect_types: int = 0,
|
||||
isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
|
||||
check_same_thread: bool = True,
|
||||
*,
|
||||
factory: type[_ConnectionT],
|
||||
cached_statements: int = 128,
|
||||
uri: bool = False,
|
||||
autocommit: bool = ...,
|
||||
) -> _ConnectionT: ...
|
||||
|
||||
else:
|
||||
@overload
|
||||
def connect(
|
||||
database: StrOrBytesPath,
|
||||
timeout: float = 5.0,
|
||||
detect_types: int = 0,
|
||||
isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
|
||||
check_same_thread: bool = True,
|
||||
cached_statements: int = 128,
|
||||
uri: bool = False,
|
||||
) -> Connection: ...
|
||||
@overload
|
||||
def connect(
|
||||
database: StrOrBytesPath,
|
||||
timeout: float,
|
||||
detect_types: int,
|
||||
isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None,
|
||||
check_same_thread: bool,
|
||||
factory: type[_ConnectionT],
|
||||
cached_statements: int = 128,
|
||||
uri: bool = False,
|
||||
) -> _ConnectionT: ...
|
||||
@overload
|
||||
def connect(
|
||||
database: StrOrBytesPath,
|
||||
timeout: float = 5.0,
|
||||
detect_types: int = 0,
|
||||
isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED",
|
||||
check_same_thread: bool = True,
|
||||
*,
|
||||
factory: type[_ConnectionT],
|
||||
cached_statements: int = 128,
|
||||
uri: bool = False,
|
||||
) -> _ConnectionT: ...
|
||||
|
||||
def enable_callback_tracebacks(enable: bool, /) -> None: ...
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
# takes a pos-or-keyword argument because there is a C wrapper
|
||||
def enable_shared_cache(do_enable: int) -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
def register_adapter(type: type[_T], adapter: _Adapter[_T], /) -> None: ...
|
||||
def register_converter(typename: str, converter: _Converter, /) -> None: ...
|
||||
|
||||
else:
|
||||
def register_adapter(type: type[_T], caster: _Adapter[_T], /) -> None: ...
|
||||
def register_converter(name: str, converter: _Converter, /) -> None: ...
|
||||
|
||||
if sys.version_info < (3, 10):
|
||||
OptimizedUnicode = str
|
||||
292
crates/red_knot_vendored/vendor/typeshed/stdlib/_ssl.pyi
vendored
Normal file
292
crates/red_knot_vendored/vendor/typeshed/stdlib/_ssl.pyi
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer, StrOrBytesPath
|
||||
from collections.abc import Callable
|
||||
from ssl import (
|
||||
SSLCertVerificationError as SSLCertVerificationError,
|
||||
SSLContext,
|
||||
SSLEOFError as SSLEOFError,
|
||||
SSLError as SSLError,
|
||||
SSLObject,
|
||||
SSLSyscallError as SSLSyscallError,
|
||||
SSLWantReadError as SSLWantReadError,
|
||||
SSLWantWriteError as SSLWantWriteError,
|
||||
SSLZeroReturnError as SSLZeroReturnError,
|
||||
)
|
||||
from typing import Any, Literal, TypedDict, final, overload
|
||||
from typing_extensions import NotRequired, Self, TypeAlias
|
||||
|
||||
_PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray
|
||||
_PCTRTT: TypeAlias = tuple[tuple[str, str], ...]
|
||||
_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...]
|
||||
_PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT]
|
||||
|
||||
class _Cipher(TypedDict):
|
||||
aead: bool
|
||||
alg_bits: int
|
||||
auth: str
|
||||
description: str
|
||||
digest: str | None
|
||||
id: int
|
||||
kea: str
|
||||
name: str
|
||||
protocol: str
|
||||
strength_bits: int
|
||||
symmetric: str
|
||||
|
||||
class _CertInfo(TypedDict):
|
||||
subject: tuple[tuple[tuple[str, str], ...], ...]
|
||||
issuer: tuple[tuple[tuple[str, str], ...], ...]
|
||||
version: int
|
||||
serialNumber: str
|
||||
notBefore: str
|
||||
notAfter: str
|
||||
subjectAltName: NotRequired[tuple[tuple[str, str], ...] | None]
|
||||
OCSP: NotRequired[tuple[str, ...] | None]
|
||||
caIssuers: NotRequired[tuple[str, ...] | None]
|
||||
crlDistributionPoints: NotRequired[tuple[str, ...] | None]
|
||||
|
||||
def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None: ...
|
||||
def RAND_bytes(n: int, /) -> bytes: ...
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]: ...
|
||||
|
||||
if sys.version_info < (3, 10):
|
||||
def RAND_egd(path: str) -> None: ...
|
||||
|
||||
def RAND_status() -> bool: ...
|
||||
def get_default_verify_paths() -> tuple[str, str, str, str]: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
_EnumRetType: TypeAlias = list[tuple[bytes, str, set[str] | bool]]
|
||||
def enum_certificates(store_name: str) -> _EnumRetType: ...
|
||||
def enum_crls(store_name: str) -> _EnumRetType: ...
|
||||
|
||||
def txt2obj(txt: str, name: bool = False) -> tuple[int, str, str, str]: ...
|
||||
def nid2obj(nid: int, /) -> tuple[int, str, str, str]: ...
|
||||
|
||||
class _SSLContext:
|
||||
check_hostname: bool
|
||||
keylog_filename: str | None
|
||||
maximum_version: int
|
||||
minimum_version: int
|
||||
num_tickets: int
|
||||
options: int
|
||||
post_handshake_auth: bool
|
||||
protocol: int
|
||||
if sys.version_info >= (3, 10):
|
||||
security_level: int
|
||||
sni_callback: Callable[[SSLObject, str, SSLContext], None | int] | None
|
||||
verify_flags: int
|
||||
verify_mode: int
|
||||
def __new__(cls, protocol: int, /) -> Self: ...
|
||||
def cert_store_stats(self) -> dict[str, int]: ...
|
||||
@overload
|
||||
def get_ca_certs(self, binary_form: Literal[False] = False) -> list[_PeerCertRetDictType]: ...
|
||||
@overload
|
||||
def get_ca_certs(self, binary_form: Literal[True]) -> list[bytes]: ...
|
||||
@overload
|
||||
def get_ca_certs(self, binary_form: bool = False) -> Any: ...
|
||||
def get_ciphers(self) -> list[_Cipher]: ...
|
||||
def load_cert_chain(
|
||||
self, certfile: StrOrBytesPath, keyfile: StrOrBytesPath | None = None, password: _PasswordType | None = None
|
||||
) -> None: ...
|
||||
def load_dh_params(self, path: str, /) -> None: ...
|
||||
def load_verify_locations(
|
||||
self,
|
||||
cafile: StrOrBytesPath | None = None,
|
||||
capath: StrOrBytesPath | None = None,
|
||||
cadata: str | ReadableBuffer | None = None,
|
||||
) -> None: ...
|
||||
def session_stats(self) -> dict[str, int]: ...
|
||||
def set_ciphers(self, cipherlist: str, /) -> None: ...
|
||||
def set_default_verify_paths(self) -> None: ...
|
||||
def set_ecdh_curve(self, name: str, /) -> None: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def set_psk_client_callback(self, callback: Callable[[str | None], tuple[str | None, bytes]] | None) -> None: ...
|
||||
def set_psk_server_callback(
|
||||
self, callback: Callable[[str | None], tuple[str | None, bytes]] | None, identity_hint: str | None = None
|
||||
) -> None: ...
|
||||
|
||||
@final
|
||||
class MemoryBIO:
|
||||
eof: bool
|
||||
pending: int
|
||||
def __new__(self) -> Self: ...
|
||||
def read(self, size: int = -1, /) -> bytes: ...
|
||||
def write(self, b: ReadableBuffer, /) -> int: ...
|
||||
def write_eof(self) -> None: ...
|
||||
|
||||
@final
|
||||
class SSLSession:
|
||||
@property
|
||||
def has_ticket(self) -> bool: ...
|
||||
@property
|
||||
def id(self) -> bytes: ...
|
||||
@property
|
||||
def ticket_lifetime_hint(self) -> int: ...
|
||||
@property
|
||||
def time(self) -> int: ...
|
||||
@property
|
||||
def timeout(self) -> int: ...
|
||||
|
||||
# _ssl.Certificate is weird: it can't be instantiated or subclassed.
|
||||
# Instances can only be created via methods of the private _ssl._SSLSocket class,
|
||||
# for which the relevant method signatures are:
|
||||
#
|
||||
# class _SSLSocket:
|
||||
# def get_unverified_chain(self) -> list[Certificate] | None: ...
|
||||
# def get_verified_chain(self) -> list[Certificate] | None: ...
|
||||
#
|
||||
# You can find a _ssl._SSLSocket object as the _sslobj attribute of a ssl.SSLSocket object
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
@final
|
||||
class Certificate:
|
||||
def get_info(self) -> _CertInfo: ...
|
||||
@overload
|
||||
def public_bytes(self) -> str: ...
|
||||
@overload
|
||||
def public_bytes(self, format: Literal[1] = 1, /) -> str: ... # ENCODING_PEM
|
||||
@overload
|
||||
def public_bytes(self, format: Literal[2], /) -> bytes: ... # ENCODING_DER
|
||||
@overload
|
||||
def public_bytes(self, format: int, /) -> str | bytes: ...
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
err_codes_to_names: dict[tuple[int, int], str]
|
||||
err_names_to_codes: dict[str, tuple[int, int]]
|
||||
lib_codes_to_names: dict[int, str]
|
||||
|
||||
_DEFAULT_CIPHERS: str
|
||||
|
||||
# SSL error numbers
|
||||
SSL_ERROR_ZERO_RETURN: int
|
||||
SSL_ERROR_WANT_READ: int
|
||||
SSL_ERROR_WANT_WRITE: int
|
||||
SSL_ERROR_WANT_X509_LOOKUP: int
|
||||
SSL_ERROR_SYSCALL: int
|
||||
SSL_ERROR_SSL: int
|
||||
SSL_ERROR_WANT_CONNECT: int
|
||||
SSL_ERROR_EOF: int
|
||||
SSL_ERROR_INVALID_ERROR_CODE: int
|
||||
|
||||
# verify modes
|
||||
CERT_NONE: int
|
||||
CERT_OPTIONAL: int
|
||||
CERT_REQUIRED: int
|
||||
|
||||
# verify flags
|
||||
VERIFY_DEFAULT: int
|
||||
VERIFY_CRL_CHECK_LEAF: int
|
||||
VERIFY_CRL_CHECK_CHAIN: int
|
||||
VERIFY_X509_STRICT: int
|
||||
VERIFY_X509_TRUSTED_FIRST: int
|
||||
if sys.version_info >= (3, 10):
|
||||
VERIFY_ALLOW_PROXY_CERTS: int
|
||||
VERIFY_X509_PARTIAL_CHAIN: int
|
||||
|
||||
# alert descriptions
|
||||
ALERT_DESCRIPTION_CLOSE_NOTIFY: int
|
||||
ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: int
|
||||
ALERT_DESCRIPTION_BAD_RECORD_MAC: int
|
||||
ALERT_DESCRIPTION_RECORD_OVERFLOW: int
|
||||
ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: int
|
||||
ALERT_DESCRIPTION_HANDSHAKE_FAILURE: int
|
||||
ALERT_DESCRIPTION_BAD_CERTIFICATE: int
|
||||
ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: int
|
||||
ALERT_DESCRIPTION_CERTIFICATE_REVOKED: int
|
||||
ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: int
|
||||
ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: int
|
||||
ALERT_DESCRIPTION_ILLEGAL_PARAMETER: int
|
||||
ALERT_DESCRIPTION_UNKNOWN_CA: int
|
||||
ALERT_DESCRIPTION_ACCESS_DENIED: int
|
||||
ALERT_DESCRIPTION_DECODE_ERROR: int
|
||||
ALERT_DESCRIPTION_DECRYPT_ERROR: int
|
||||
ALERT_DESCRIPTION_PROTOCOL_VERSION: int
|
||||
ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: int
|
||||
ALERT_DESCRIPTION_INTERNAL_ERROR: int
|
||||
ALERT_DESCRIPTION_USER_CANCELLED: int
|
||||
ALERT_DESCRIPTION_NO_RENEGOTIATION: int
|
||||
ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: int
|
||||
ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: int
|
||||
ALERT_DESCRIPTION_UNRECOGNIZED_NAME: int
|
||||
ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: int
|
||||
ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: int
|
||||
ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: int
|
||||
|
||||
# protocol versions
|
||||
PROTOCOL_SSLv23: int
|
||||
PROTOCOL_TLS: int
|
||||
PROTOCOL_TLS_CLIENT: int
|
||||
PROTOCOL_TLS_SERVER: int
|
||||
PROTOCOL_TLSv1: int
|
||||
PROTOCOL_TLSv1_1: int
|
||||
PROTOCOL_TLSv1_2: int
|
||||
|
||||
# protocol options
|
||||
OP_ALL: int
|
||||
OP_NO_SSLv2: int
|
||||
OP_NO_SSLv3: int
|
||||
OP_NO_TLSv1: int
|
||||
OP_NO_TLSv1_1: int
|
||||
OP_NO_TLSv1_2: int
|
||||
OP_NO_TLSv1_3: int
|
||||
OP_CIPHER_SERVER_PREFERENCE: int
|
||||
OP_SINGLE_DH_USE: int
|
||||
OP_NO_TICKET: int
|
||||
OP_SINGLE_ECDH_USE: int
|
||||
OP_NO_COMPRESSION: int
|
||||
OP_ENABLE_MIDDLEBOX_COMPAT: int
|
||||
OP_NO_RENEGOTIATION: int
|
||||
if sys.version_info >= (3, 11):
|
||||
OP_IGNORE_UNEXPECTED_EOF: int
|
||||
elif sys.version_info >= (3, 8) and sys.platform == "linux":
|
||||
OP_IGNORE_UNEXPECTED_EOF: int
|
||||
if sys.version_info >= (3, 12):
|
||||
OP_LEGACY_SERVER_CONNECT: int
|
||||
OP_ENABLE_KTLS: int
|
||||
|
||||
# host flags
|
||||
HOSTFLAG_ALWAYS_CHECK_SUBJECT: int
|
||||
HOSTFLAG_NEVER_CHECK_SUBJECT: int
|
||||
HOSTFLAG_NO_WILDCARDS: int
|
||||
HOSTFLAG_NO_PARTIAL_WILDCARDS: int
|
||||
HOSTFLAG_MULTI_LABEL_WILDCARDS: int
|
||||
HOSTFLAG_SINGLE_LABEL_SUBDOMAINS: int
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
# certificate file types
|
||||
# Typed as Literal so the overload on Certificate.public_bytes can work properly.
|
||||
ENCODING_PEM: Literal[1]
|
||||
ENCODING_DER: Literal[2]
|
||||
|
||||
# protocol versions
|
||||
PROTO_MINIMUM_SUPPORTED: int
|
||||
PROTO_MAXIMUM_SUPPORTED: int
|
||||
PROTO_SSLv3: int
|
||||
PROTO_TLSv1: int
|
||||
PROTO_TLSv1_1: int
|
||||
PROTO_TLSv1_2: int
|
||||
PROTO_TLSv1_3: int
|
||||
|
||||
# feature support
|
||||
HAS_SNI: bool
|
||||
HAS_TLS_UNIQUE: bool
|
||||
HAS_ECDH: bool
|
||||
HAS_NPN: bool
|
||||
if sys.version_info >= (3, 13):
|
||||
HAS_PSK: bool
|
||||
HAS_ALPN: bool
|
||||
HAS_SSLv2: bool
|
||||
HAS_SSLv3: bool
|
||||
HAS_TLSv1: bool
|
||||
HAS_TLSv1_1: bool
|
||||
HAS_TLSv1_2: bool
|
||||
HAS_TLSv1_3: bool
|
||||
|
||||
# version info
|
||||
OPENSSL_VERSION_NUMBER: int
|
||||
OPENSSL_VERSION_INFO: tuple[int, int, int, int, int]
|
||||
OPENSSL_VERSION: str
|
||||
_OPENSSL_API_VERSION: tuple[int, int, int, int, int]
|
||||
@@ -1,37 +1,10 @@
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from typing import Any, Generic, TypeVar, final, overload
|
||||
from typing_extensions import Self
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from types import GenericAlias
|
||||
from typing import Any, TypeVar, overload
|
||||
from weakref import CallableProxyType as CallableProxyType, ProxyType as ProxyType, ReferenceType as ReferenceType, ref as ref
|
||||
|
||||
_C = TypeVar("_C", bound=Callable[..., Any])
|
||||
_T = TypeVar("_T")
|
||||
|
||||
@final
|
||||
class CallableProxyType(Generic[_C]): # "weakcallableproxy"
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __getattr__(self, attr: str) -> Any: ...
|
||||
__call__: _C
|
||||
|
||||
@final
|
||||
class ProxyType(Generic[_T]): # "weakproxy"
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __getattr__(self, attr: str) -> Any: ...
|
||||
|
||||
class ReferenceType(Generic[_T]):
|
||||
__callback__: Callable[[Self], Any]
|
||||
def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ...
|
||||
def __init__(self, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> None: ...
|
||||
def __call__(self) -> _T | None: ...
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __hash__(self) -> int: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
|
||||
|
||||
ref = ReferenceType
|
||||
|
||||
def getweakrefcount(object: Any, /) -> int: ...
|
||||
def getweakrefs(object: Any, /) -> list[Any]: ...
|
||||
|
||||
|
||||
1989
crates/red_knot_vendored/vendor/typeshed/stdlib/ast.pyi
vendored
1989
crates/red_knot_vendored/vendor/typeshed/stdlib/ast.pyi
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,11 @@
|
||||
import ssl
|
||||
import sys
|
||||
from _asyncio import (
|
||||
_get_running_loop as _get_running_loop,
|
||||
_set_running_loop as _set_running_loop,
|
||||
get_event_loop as get_event_loop,
|
||||
get_running_loop as get_running_loop,
|
||||
)
|
||||
from _typeshed import FileDescriptorLike, ReadableBuffer, StrPath, Unused, WriteableBuffer
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import Callable, Sequence
|
||||
@@ -632,7 +638,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta):
|
||||
|
||||
def get_event_loop_policy() -> AbstractEventLoopPolicy: ...
|
||||
def set_event_loop_policy(policy: AbstractEventLoopPolicy | None) -> None: ...
|
||||
def get_event_loop() -> AbstractEventLoop: ...
|
||||
def set_event_loop(loop: AbstractEventLoop | None) -> None: ...
|
||||
def new_event_loop() -> AbstractEventLoop: ...
|
||||
|
||||
@@ -646,7 +651,3 @@ if sys.version_info < (3, 14):
|
||||
else:
|
||||
def get_child_watcher() -> AbstractChildWatcher: ...
|
||||
def set_child_watcher(watcher: AbstractChildWatcher) -> None: ...
|
||||
|
||||
def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ...
|
||||
def _get_running_loop() -> AbstractEventLoop: ...
|
||||
def get_running_loop() -> AbstractEventLoop: ...
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import sys
|
||||
from collections.abc import Awaitable, Callable, Generator, Iterable
|
||||
from _asyncio import Future as Future
|
||||
from concurrent.futures._base import Future as _ConcurrentFuture
|
||||
from contextvars import Context
|
||||
from typing import Any, Literal, TypeVar
|
||||
from typing_extensions import Self, TypeIs
|
||||
from typing import Any, TypeVar
|
||||
from typing_extensions import TypeIs
|
||||
|
||||
from .events import AbstractEventLoop
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from types import GenericAlias
|
||||
|
||||
__all__ = ("Future", "wrap_future", "isfuture")
|
||||
|
||||
_T = TypeVar("_T")
|
||||
@@ -18,40 +13,4 @@ _T = TypeVar("_T")
|
||||
# but it leads to circular import error in pytype tool.
|
||||
# That's why the import order is reversed.
|
||||
def isfuture(obj: object) -> TypeIs[Future[Any]]: ...
|
||||
|
||||
class Future(Awaitable[_T], Iterable[_T]):
|
||||
_state: str
|
||||
@property
|
||||
def _exception(self) -> BaseException | None: ...
|
||||
_blocking: bool
|
||||
@property
|
||||
def _log_traceback(self) -> bool: ...
|
||||
@_log_traceback.setter
|
||||
def _log_traceback(self, val: Literal[False]) -> None: ...
|
||||
_asyncio_future_blocking: bool # is a part of duck-typing contract for `Future`
|
||||
def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ...
|
||||
def __del__(self) -> None: ...
|
||||
def get_loop(self) -> AbstractEventLoop: ...
|
||||
@property
|
||||
def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ...
|
||||
def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def cancel(self, msg: Any | None = None) -> bool: ...
|
||||
else:
|
||||
def cancel(self) -> bool: ...
|
||||
|
||||
def cancelled(self) -> bool: ...
|
||||
def done(self) -> bool: ...
|
||||
def result(self) -> _T: ...
|
||||
def exception(self) -> BaseException | None: ...
|
||||
def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ...
|
||||
def set_result(self, result: _T, /) -> None: ...
|
||||
def set_exception(self, exception: type | BaseException, /) -> None: ...
|
||||
def __iter__(self) -> Generator[Any, None, _T]: ...
|
||||
def __await__(self) -> Generator[Any, None, _T]: ...
|
||||
@property
|
||||
def _loop(self) -> AbstractEventLoop: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
|
||||
|
||||
def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ...
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user