Compare commits
65 Commits
david/unre
...
0.8.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0e26e6fc8 | ||
|
|
e9941cd714 | ||
|
|
43bf1a8907 | ||
|
|
fda8b1f884 | ||
|
|
2d3f557875 | ||
|
|
bd27bfab5d | ||
|
|
155d34bbb9 | ||
|
|
04c887c8fc | ||
|
|
af43bd4b0f | ||
|
|
614917769e | ||
|
|
8b23086eac | ||
|
|
948549fcdc | ||
|
|
e67f7f243d | ||
|
|
c617b2a48a | ||
|
|
1685d95ed2 | ||
|
|
575deb5d4d | ||
|
|
edce559431 | ||
|
|
62e358e929 | ||
|
|
81bfcc9899 | ||
|
|
74309008fd | ||
|
|
a255d79087 | ||
|
|
70bd10614f | ||
|
|
bf0fd04e4e | ||
|
|
a69dfd4a74 | ||
|
|
c2e17d0399 | ||
|
|
10fef8bd5d | ||
|
|
246a6df87d | ||
|
|
3e702e12f7 | ||
|
|
91e2d9a139 | ||
|
|
5137fcc9c8 | ||
|
|
83651deac7 | ||
|
|
6dfe125f44 | ||
|
|
f96dfc179f | ||
|
|
76d2e56501 | ||
|
|
30d80d9746 | ||
|
|
5a67d3269b | ||
|
|
02d1e6a94a | ||
|
|
48ec3a8add | ||
|
|
289a938ae8 | ||
|
|
3e5ab6cf38 | ||
|
|
48d33595b9 | ||
|
|
23ee7a954e | ||
|
|
d4a7c098dc | ||
|
|
0c5f03a059 | ||
|
|
239bfb6de7 | ||
|
|
3c3ec6755c | ||
|
|
4c05f2c8b4 | ||
|
|
d594796e3a | ||
|
|
b5ef2844ef | ||
|
|
06183bd8a1 | ||
|
|
4068006c5f | ||
|
|
145c97c94f | ||
|
|
84748be163 | ||
|
|
9e017634cb | ||
|
|
56ae73a925 | ||
|
|
be07424e80 | ||
|
|
579ef01294 | ||
|
|
90487b8cbd | ||
|
|
f3d8c023d3 | ||
|
|
b63c2e126b | ||
|
|
a6402fb51e | ||
|
|
b3b2c982cd | ||
|
|
abb3c6ea95 | ||
|
|
224fe75a76 | ||
|
|
dc29f52750 |
13
.github/workflows/ci.yaml
vendored
13
.github/workflows/ci.yaml
vendored
@@ -32,6 +32,8 @@ jobs:
|
||||
# Flag that is raised when any code is changed
|
||||
# This is superset of the linter and formatter
|
||||
code: ${{ steps.changed.outputs.code_any_changed }}
|
||||
# Flag that is raised when any code that affects the fuzzer is changed
|
||||
fuzz: ${{ steps.changed.outputs.fuzz_any_changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -79,6 +81,11 @@ jobs:
|
||||
- python/**
|
||||
- .github/workflows/ci.yaml
|
||||
|
||||
fuzz:
|
||||
- fuzz/Cargo.toml
|
||||
- fuzz/Cargo.lock
|
||||
- fuzz/fuzz_targets/**
|
||||
|
||||
code:
|
||||
- "**/*"
|
||||
- "!**/*.md"
|
||||
@@ -288,7 +295,7 @@ jobs:
|
||||
name: "cargo fuzz build"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
if: ${{ github.ref == 'refs/heads/main' || needs.determine_changes.outputs.fuzz == 'true' }}
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -383,7 +390,7 @@ jobs:
|
||||
name: ruff
|
||||
path: target/debug
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v7
|
||||
name: Download baseline Ruff binary
|
||||
with:
|
||||
name: ruff
|
||||
@@ -559,7 +566,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v3
|
||||
uses: astral-sh/setup-uv@v4
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
|
||||
4
.github/workflows/pr-comment.yaml
vendored
4
.github/workflows/pr-comment.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v7
|
||||
name: Download pull request number
|
||||
with:
|
||||
name: pr-number
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
echo "pr-number=$(<pr-number)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v7
|
||||
name: "Download ecosystem results"
|
||||
id: download-ecosystem-result
|
||||
if: steps.pr-number.outputs.pr-number
|
||||
|
||||
2
.github/workflows/publish-playground.yml
vendored
2
.github/workflows/publish-playground.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.12.1
|
||||
uses: cloudflare/wrangler-action@v3.13.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
2
.github/workflows/publish-pypi.yml
vendored
2
.github/workflows/publish-pypi.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@v3
|
||||
uses: astral-sh/setup-uv@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: wheels-*
|
||||
|
||||
@@ -22,7 +22,7 @@ repos:
|
||||
- id: validate-pyproject
|
||||
|
||||
- repo: https://github.com/executablebooks/mdformat
|
||||
rev: 0.7.18
|
||||
rev: 0.7.19
|
||||
hooks:
|
||||
- id: mdformat
|
||||
additional_dependencies:
|
||||
@@ -36,7 +36,7 @@ repos:
|
||||
)$
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.42.0
|
||||
rev: v0.43.0
|
||||
hooks:
|
||||
- id: markdownlint-fix
|
||||
exclude: |
|
||||
@@ -59,7 +59,7 @@ repos:
|
||||
- black==24.10.0
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.27.3
|
||||
rev: v1.28.1
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
@@ -73,7 +73,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.7.4
|
||||
rev: v0.8.1
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
@@ -83,7 +83,7 @@ repos:
|
||||
|
||||
# Prettier
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: v3.3.3
|
||||
rev: v3.4.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
types: [yaml]
|
||||
|
||||
@@ -192,7 +192,7 @@ flag or `unsafe-fixes` configuration option can be used to enable unsafe fixes.
|
||||
|
||||
See the [docs](https://docs.astral.sh/ruff/configuration/#fix-safety) for details.
|
||||
|
||||
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
|
||||
Previously, Ruff enabled all implemented rules in Pycodestyle (`E`) by default. Ruff now only includes the
|
||||
Pycodestyle prefixes `E4`, `E7`, and `E9` to exclude rules that conflict with automatic formatters. Consequently,
|
||||
|
||||
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,5 +1,42 @@
|
||||
# Changelog
|
||||
|
||||
## 0.8.2
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`airflow`\] Avoid deprecated values (`AIR302`) ([#14582](https://github.com/astral-sh/ruff/pull/14582))
|
||||
- \[`airflow`\] Extend removed names for `AIR302` ([#14734](https://github.com/astral-sh/ruff/pull/14734))
|
||||
- \[`ruff`\] Extend `unnecessary-regular-expression` to non-literal strings (`RUF055`) ([#14679](https://github.com/astral-sh/ruff/pull/14679))
|
||||
- \[`ruff`\] Implement `used-dummy-variable` (`RUF052`) ([#14611](https://github.com/astral-sh/ruff/pull/14611))
|
||||
- \[`ruff`\] Implement `unnecessary-cast-to-int` (`RUF046`) ([#14697](https://github.com/astral-sh/ruff/pull/14697))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`airflow`\] Check `AIR001` from builtin or providers `operators` module ([#14631](https://github.com/astral-sh/ruff/pull/14631))
|
||||
- \[`flake8-pytest-style`\] Remove `@` in `pytest.mark.parametrize` rule messages ([#14770](https://github.com/astral-sh/ruff/pull/14770))
|
||||
- \[`pandas-vet`\] Skip rules if the `panda` module hasn't been seen ([#14671](https://github.com/astral-sh/ruff/pull/14671))
|
||||
- \[`pylint`\] Fix false negatives for `ascii` and `sorted` in `len-as-condition` (`PLC1802`) ([#14692](https://github.com/astral-sh/ruff/pull/14692))
|
||||
- \[`refurb`\] Guard `hashlib` imports and mark `hashlib-digest-hex` fix as safe (`FURB181`) ([#14694](https://github.com/astral-sh/ruff/pull/14694))
|
||||
|
||||
### Configuration
|
||||
|
||||
- \[`flake8-import-conventions`\] Improve syntax check for aliases supplied in configuration for `unconventional-import-alias` (`ICN001`) ([#14745](https://github.com/astral-sh/ruff/pull/14745))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Revert: [pyflakes] Avoid false positives in `@no_type_check` contexts (`F821`, `F722`) (#14615) ([#14726](https://github.com/astral-sh/ruff/pull/14726))
|
||||
- \[`pep8-naming`\] Avoid false positive for `class Bar(type(foo))` (`N804`) ([#14683](https://github.com/astral-sh/ruff/pull/14683))
|
||||
- \[`pycodestyle`\] Handle f-strings properly for `invalid-escape-sequence` (`W605`) ([#14748](https://github.com/astral-sh/ruff/pull/14748))
|
||||
- \[`pylint`\] Ignore `@overload` in `PLR0904` ([#14730](https://github.com/astral-sh/ruff/pull/14730))
|
||||
- \[`refurb`\] Handle non-finite decimals in `verbose-decimal-constructor` (`FURB157`) ([#14596](https://github.com/astral-sh/ruff/pull/14596))
|
||||
- \[`ruff`\] Avoid emitting `assignment-in-assert` when all references to the assigned variable are themselves inside `assert`s (`RUF018`) ([#14661](https://github.com/astral-sh/ruff/pull/14661))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Improve docs for `flake8-use-pathlib` rules ([#14741](https://github.com/astral-sh/ruff/pull/14741))
|
||||
- Improve error messages and docs for `flake8-comprehensions` rules ([#14729](https://github.com/astral-sh/ruff/pull/14729))
|
||||
- \[`flake8-type-checking`\] Expands `TC006` docs to better explain itself ([#14749](https://github.com/astral-sh/ruff/pull/14749))
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Preview features
|
||||
@@ -21,6 +58,7 @@
|
||||
- \[`ruff`\] Auto-add `r` prefix when string has no backslashes for `unraw-re-pattern` (`RUF039`) ([#14536](https://github.com/astral-sh/ruff/pull/14536))
|
||||
- \[`ruff`\] Implement `invalid-assert-message-literal-argument` (`RUF040`) ([#14488](https://github.com/astral-sh/ruff/pull/14488))
|
||||
- \[`ruff`\] Implement `unnecessary-nested-literal` (`RUF041`) ([#14323](https://github.com/astral-sh/ruff/pull/14323))
|
||||
- \[`ruff`\] Implement `unnecessary-regular-expression` (`RUF055`) ([#14659](https://github.com/astral-sh/ruff/pull/14659))
|
||||
|
||||
### Rule changes
|
||||
|
||||
@@ -34,6 +72,8 @@
|
||||
|
||||
- Avoid fixing code to `None | None` for `redundant-none-literal` (`PYI061`) and `never-union` (`RUF020`) ([#14583](https://github.com/astral-sh/ruff/pull/14583), [#14589](https://github.com/astral-sh/ruff/pull/14589))
|
||||
- \[`flake8-bugbear`\] Fix `mutable-contextvar-default` to resolve annotated function calls properly (`B039`) ([#14532](https://github.com/astral-sh/ruff/pull/14532))
|
||||
- \[`flake8-pyi`, `ruff`\] Fix traversal of nested literals and unions (`PYI016`, `PYI051`, `PYI055`, `PYI062`, `RUF041`) ([#14641](https://github.com/astral-sh/ruff/pull/14641))
|
||||
- \[`flake8-pyi`\] Avoid rewriting invalid type expressions in `unnecessary-type-union` (`PYI055`) ([#14660](https://github.com/astral-sh/ruff/pull/14660))
|
||||
- \[`flake8-type-checking`\] Avoid syntax errors and type checking problem for quoted annotations autofix (`TC003`, `TC006`) ([#14634](https://github.com/astral-sh/ruff/pull/14634))
|
||||
- \[`pylint`\] Do not wrap function calls in parentheses in the fix for unnecessary-dunder-call (`PLC2801`) ([#14601](https://github.com/astral-sh/ruff/pull/14601))
|
||||
- \[`ruff`\] Handle `attrs`'s `auto_attribs` correctly (`RUF009`) ([#14520](https://github.com/astral-sh/ruff/pull/14520))
|
||||
@@ -254,7 +294,7 @@ The following fixes have been stabilized:
|
||||
### Preview features
|
||||
|
||||
- Fix `E221` and `E222` to flag missing or extra whitespace around `==` operator ([#13890](https://github.com/astral-sh/ruff/pull/13890))
|
||||
- Formatter: Alternate quotes for strings inside f-strings in preview ([#13860](https://github.com/astral-sh/ruff/pull/13860))
|
||||
- Formatter: Alternate quotes for strings inside f-strings in preview ([#13860](https://github.com/astral-sh/ruff/pull/13860))
|
||||
- Formatter: Join implicit concatenated strings when they fit on a line ([#13663](https://github.com/astral-sh/ruff/pull/13663))
|
||||
- \[`pylint`\] Restrict `iteration-over-set` to only work on sets of literals (`PLC0208`) ([#13731](https://github.com/astral-sh/ruff/pull/13731))
|
||||
|
||||
@@ -1269,7 +1309,7 @@ To read more about this exciting milestone, check out our [blog post](https://as
|
||||
### Preview features
|
||||
|
||||
- \[`pycodestyle`\] Ignore end-of-line comments when determining blank line rules ([#11342](https://github.com/astral-sh/ruff/pull/11342))
|
||||
- \[`pylint`\] Detect `pathlib.Path.open` calls in `unspecified-encoding` (`PLW1514`) ([#11288](https://github.com/astral-sh/ruff/pull/11288))
|
||||
- \[`pylint`\] Detect `pathlib.Path.open` calls in `unspecified-encoding` (`PLW1514`) ([#11288](https://github.com/astral-sh/ruff/pull/11288))
|
||||
- \[`flake8-pyi`\] Implement `PYI059` (`generic-not-last-base-class`) ([#11233](https://github.com/astral-sh/ruff/pull/11233))
|
||||
- \[`flake8-pyi`\] Implement `PYI062` (`duplicate-literal-member`) ([#11269](https://github.com/astral-sh/ruff/pull/11269))
|
||||
|
||||
@@ -1644,7 +1684,7 @@ To setup `ruff server` with your editor, refer to the [README.md](https://github
|
||||
- \[`pycodestyle`\] Do not ignore lines before the first logical line in blank lines rules. ([#10382](https://github.com/astral-sh/ruff/pull/10382))
|
||||
- \[`pycodestyle`\] Do not trigger `E225` and `E275` when the next token is a ')' ([#10315](https://github.com/astral-sh/ruff/pull/10315))
|
||||
- \[`pylint`\] Avoid false-positive slot non-assignment for `__dict__` (`PLE0237`) ([#10348](https://github.com/astral-sh/ruff/pull/10348))
|
||||
- Gate f-string struct size test for Rustc \< 1.76 ([#10371](https://github.com/astral-sh/ruff/pull/10371))
|
||||
- Gate f-string struct size test for Rustc < 1.76 ([#10371](https://github.com/astral-sh/ruff/pull/10371))
|
||||
|
||||
### Documentation
|
||||
|
||||
|
||||
@@ -863,7 +863,7 @@ each configuration file.
|
||||
|
||||
The package root is used to determine a file's "module path". Consider, again, `baz.py`. In that
|
||||
case, `./my_project/src/foo` was identified as the package root, so the module path for `baz.py`
|
||||
would resolve to `foo.bar.baz` — as computed by taking the relative path from the package root
|
||||
would resolve to `foo.bar.baz` — as computed by taking the relative path from the package root
|
||||
(inclusive of the root itself). The module path can be thought of as "the path you would use to
|
||||
import the module" (e.g., `import foo.bar.baz`).
|
||||
|
||||
|
||||
243
Cargo.lock
generated
243
Cargo.lock
generated
@@ -413,7 +413,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -693,7 +693,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -704,7 +704,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -774,7 +774,7 @@ dependencies = [
|
||||
"glob",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -826,7 +826,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1246,7 +1246,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1314,9 +1314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
@@ -1420,7 +1420,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1485,10 +1485,11 @@ checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||
checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -1520,9 +1521,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.164"
|
||||
version = "0.2.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
@@ -1546,7 +1547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2ae40017ac09cd2c6a53504cb3c871c7f2b41466eac5bc66ba63f39073b467b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1833,9 +1834,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ordermap"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f2bd7b03bf2c767e1bb7b91505dbe022833776e60480275e6f2fb0db0c7503"
|
||||
checksum = "f80a48eb68b6a7da9829b8b0429011708f775af80676a91063d023a66a656106"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
@@ -1910,9 +1911,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
|
||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||
|
||||
[[package]]
|
||||
name = "peg"
|
||||
@@ -1965,7 +1966,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"pep440_rs",
|
||||
"regex",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thiserror 1.0.67",
|
||||
@@ -2012,7 +2013,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2172,6 +2173,26 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
|
||||
dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck_macros"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
@@ -2272,6 +2293,8 @@ dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"memchr",
|
||||
"ordermap",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"red_knot_test",
|
||||
"red_knot_vendored",
|
||||
"ruff_db",
|
||||
@@ -2282,7 +2305,7 @@ dependencies = [
|
||||
"ruff_python_stdlib",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"serde",
|
||||
"smallvec",
|
||||
@@ -2309,7 +2332,7 @@ dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
@@ -2322,6 +2345,7 @@ name = "red_knot_test"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"camino",
|
||||
"colored",
|
||||
"memchr",
|
||||
"red_knot_python_semantic",
|
||||
@@ -2332,7 +2356,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"smallvec",
|
||||
]
|
||||
@@ -2380,7 +2404,7 @@ dependencies = [
|
||||
"ruff_db",
|
||||
"ruff_python_ast",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"serde",
|
||||
"thiserror 2.0.3",
|
||||
@@ -2489,7 +2513,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2529,7 +2553,7 @@ dependencies = [
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"ruff_workspace",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
@@ -2560,7 +2584,7 @@ dependencies = [
|
||||
"ruff_python_formatter",
|
||||
"ruff_python_parser",
|
||||
"ruff_python_trivia",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tikv-jemallocator",
|
||||
@@ -2602,7 +2626,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"serde",
|
||||
"tempfile",
|
||||
@@ -2671,7 +2695,7 @@ dependencies = [
|
||||
"ruff_cache",
|
||||
"ruff_macros",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
@@ -2708,7 +2732,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2749,7 +2773,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2775,7 +2799,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruff_python_trivia",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2811,7 +2835,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
@@ -2857,7 +2881,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2904,7 +2928,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"static_assertions",
|
||||
"unicode-ident",
|
||||
"unicode-normalization",
|
||||
@@ -2935,7 +2959,7 @@ dependencies = [
|
||||
"ruff_python_parser",
|
||||
"ruff_python_stdlib",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
@@ -2993,7 +3017,7 @@ dependencies = [
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"ruff_workspace",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
@@ -3023,7 +3047,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
@@ -3074,7 +3098,7 @@ dependencies = [
|
||||
"ruff_python_semantic",
|
||||
"ruff_python_stdlib",
|
||||
"ruff_source_file",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"shellexpand",
|
||||
@@ -3101,9 +3125,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
@@ -3120,9 +3144,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.10"
|
||||
version = "0.23.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
|
||||
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -3135,15 +3159,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.7.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.5"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -3175,7 +3199,7 @@ dependencies = [
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"parking_lot",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa-macro-rules",
|
||||
"salsa-macros",
|
||||
"smallvec",
|
||||
@@ -3195,7 +3219,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -3229,7 +3253,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3278,7 +3302,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3289,7 +3313,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3312,7 +3336,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3353,7 +3377,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3387,9 +3411,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
|
||||
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
@@ -3461,7 +3485,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3472,9 +3496,20 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.89"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3489,7 +3524,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3552,7 +3587,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3563,7 +3598,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
@@ -3593,7 +3628,7 @@ checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3604,7 +3639,7 @@ checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3708,9 +3743,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
@@ -3720,20 +3755,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -3775,9 +3810,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.18"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term 0.46.0",
|
||||
@@ -3926,18 +3961,21 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.10.1"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a"
|
||||
checksum = "b30e6f97efe1fa43535ee241ee76967d3ff6ff3953ebb430d8d55c5393029e7b"
|
||||
dependencies = [
|
||||
"base64 0.22.0",
|
||||
"flate2",
|
||||
"litemap",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3996,7 +4034,7 @@ checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4071,9 +4109,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -4082,36 +4120,37 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.45"
|
||||
version = "0.4.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
|
||||
checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -4119,32 +4158,32 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.45"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426"
|
||||
checksum = "3d919bb60ebcecb9160afee6c71b43a58a4f0517a2de0054cd050d02cec08201"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"minicov",
|
||||
"once_cell",
|
||||
"scoped-tls",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@@ -4153,20 +4192,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.45"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0"
|
||||
checksum = "222ebde6ea87fbfa6bdd2e9f1fd8a91d60aee5db68792632176c4e16a74fc7d8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.69"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
|
||||
checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -4462,7 +4501,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4483,7 +4522,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4503,7 +4542,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4532,7 +4571,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -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.8.1/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.8.1/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.8.2/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.8.2/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.8.1
|
||||
rev: v0.8.2
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -103,7 +103,7 @@ called **once**.
|
||||
|
||||
## Profiling
|
||||
|
||||
Red Knot generates a folded stack trace to the current directory named `tracing.folded` when setting the environment variable `RED_KNOT_LOG_PROFILE` to `1` or `true`.
|
||||
Red Knot generates a folded stack trace to the current directory named `tracing.folded` when setting the environment variable `RED_KNOT_LOG_PROFILE` to `1` or `true`.
|
||||
|
||||
```bash
|
||||
RED_KNOT_LOG_PROFILE=1 red_knot -- --current-directory=../test -vvv
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
pub enum TargetVersion {
|
||||
Py37,
|
||||
Py38,
|
||||
#[default]
|
||||
Py39,
|
||||
Py310,
|
||||
Py311,
|
||||
Py312,
|
||||
#[default]
|
||||
Py313,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
#![allow(clippy::disallowed_names)]
|
||||
|
||||
use std::io::Write;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use red_knot_python_semantic::{resolve_module, ModuleName, Program, PythonVersion, SitePackages};
|
||||
use red_knot_workspace::db::{Db, RootDatabase};
|
||||
use red_knot_workspace::watch;
|
||||
use red_knot_workspace::watch::{directory_watcher, WorkspaceWatcher};
|
||||
use red_knot_workspace::watch::{directory_watcher, ChangeEvent, WorkspaceWatcher};
|
||||
use red_knot_workspace::workspace::settings::{Configuration, SearchPathConfiguration};
|
||||
use red_knot_workspace::workspace::WorkspaceMetadata;
|
||||
use ruff_db::files::{system_path_to_file, File, FileError};
|
||||
use ruff_db::source::source_text;
|
||||
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
|
||||
use ruff_db::testing::{setup_logging, setup_logging_with_filter};
|
||||
use ruff_db::Upcast;
|
||||
|
||||
struct TestCase {
|
||||
db: RootDatabase,
|
||||
watcher: Option<WorkspaceWatcher>,
|
||||
changes_receiver: crossbeam::channel::Receiver<Vec<watch::ChangeEvent>>,
|
||||
changes_receiver: crossbeam::channel::Receiver<Vec<ChangeEvent>>,
|
||||
/// The temporary directory that contains the test files.
|
||||
/// We need to hold on to it in the test case or the temp files get deleted.
|
||||
_temp_dir: tempfile::TempDir,
|
||||
@@ -40,12 +38,36 @@ impl TestCase {
|
||||
&self.db
|
||||
}
|
||||
|
||||
fn stop_watch(&mut self) -> Vec<watch::ChangeEvent> {
|
||||
self.try_stop_watch(Duration::from_secs(10))
|
||||
.expect("Expected watch changes but observed none")
|
||||
#[track_caller]
|
||||
fn stop_watch<M>(&mut self, matcher: M) -> Vec<ChangeEvent>
|
||||
where
|
||||
M: MatchEvent,
|
||||
{
|
||||
// track_caller is unstable for lambdas -> That's why this is a fn
|
||||
#[track_caller]
|
||||
fn panic_with_formatted_events(events: Vec<ChangeEvent>) -> Vec<ChangeEvent> {
|
||||
panic!(
|
||||
"Didn't observe expected change:\n{}",
|
||||
events
|
||||
.into_iter()
|
||||
.map(|event| format!(" - {event:?}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
|
||||
self.try_stop_watch(matcher, Duration::from_secs(10))
|
||||
.unwrap_or_else(panic_with_formatted_events)
|
||||
}
|
||||
|
||||
fn try_stop_watch(&mut self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
|
||||
fn try_stop_watch<M>(
|
||||
&mut self,
|
||||
mut matcher: M,
|
||||
timeout: Duration,
|
||||
) -> Result<Vec<ChangeEvent>, Vec<ChangeEvent>>
|
||||
where
|
||||
M: MatchEvent,
|
||||
{
|
||||
tracing::debug!("Try stopping watch with timeout {:?}", timeout);
|
||||
|
||||
let watcher = self
|
||||
@@ -53,36 +75,50 @@ impl TestCase {
|
||||
.take()
|
||||
.expect("Cannot call `stop_watch` more than once");
|
||||
|
||||
let mut all_events = self
|
||||
.changes_receiver
|
||||
.recv_timeout(timeout)
|
||||
.unwrap_or_default();
|
||||
let start = Instant::now();
|
||||
let mut all_events = Vec::new();
|
||||
|
||||
loop {
|
||||
let events = self
|
||||
.changes_receiver
|
||||
.recv_timeout(Duration::from_millis(100))
|
||||
.unwrap_or_default();
|
||||
|
||||
if events
|
||||
.iter()
|
||||
.any(|event| matcher.match_event(event) || event.is_rescan())
|
||||
{
|
||||
all_events.extend(events);
|
||||
break;
|
||||
}
|
||||
|
||||
all_events.extend(events);
|
||||
|
||||
if start.elapsed() > timeout {
|
||||
return Err(all_events);
|
||||
}
|
||||
}
|
||||
|
||||
watcher.flush();
|
||||
tracing::debug!("Flushed file watcher");
|
||||
watcher.stop();
|
||||
tracing::debug!("Stopping file watcher");
|
||||
|
||||
// Consume remaining events
|
||||
for event in &self.changes_receiver {
|
||||
all_events.extend(event);
|
||||
}
|
||||
|
||||
if all_events.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(all_events)
|
||||
Ok(all_events)
|
||||
}
|
||||
|
||||
fn take_watch_changes(&self) -> Vec<watch::ChangeEvent> {
|
||||
fn take_watch_changes(&self) -> Vec<ChangeEvent> {
|
||||
self.try_take_watch_changes(Duration::from_secs(10))
|
||||
.expect("Expected watch changes but observed none")
|
||||
}
|
||||
|
||||
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
|
||||
let Some(watcher) = &self.watcher else {
|
||||
return None;
|
||||
};
|
||||
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<ChangeEvent>> {
|
||||
let watcher = self.watcher.as_ref()?;
|
||||
|
||||
let mut all_events = self
|
||||
.changes_receiver
|
||||
@@ -104,7 +140,7 @@ impl TestCase {
|
||||
Some(all_events)
|
||||
}
|
||||
|
||||
fn apply_changes(&mut self, changes: Vec<watch::ChangeEvent>) {
|
||||
fn apply_changes(&mut self, changes: Vec<ChangeEvent>) {
|
||||
self.db.apply_changes(changes, Some(&self.configuration));
|
||||
}
|
||||
|
||||
@@ -140,6 +176,23 @@ impl TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
trait MatchEvent {
|
||||
fn match_event(&mut self, event: &ChangeEvent) -> bool;
|
||||
}
|
||||
|
||||
fn event_for_file(name: &str) -> impl MatchEvent + '_ {
|
||||
|event: &ChangeEvent| event.file_name() == Some(name)
|
||||
}
|
||||
|
||||
impl<F> MatchEvent for F
|
||||
where
|
||||
F: FnMut(&ChangeEvent) -> bool,
|
||||
{
|
||||
fn match_event(&mut self, event: &ChangeEvent) -> bool {
|
||||
(*self)(event)
|
||||
}
|
||||
}
|
||||
|
||||
trait SetupFiles {
|
||||
fn setup(self, root_path: &SystemPath, workspace_path: &SystemPath) -> anyhow::Result<()>;
|
||||
}
|
||||
@@ -315,7 +368,7 @@ fn new_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -338,7 +391,7 @@ fn new_ignored_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -360,7 +413,7 @@ fn changed_file() -> anyhow::Result<()> {
|
||||
|
||||
update_file(&foo_path, "print('Version 2')")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
assert!(!changes.is_empty());
|
||||
|
||||
@@ -385,7 +438,7 @@ fn deleted_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::remove_file(foo_path.as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -417,7 +470,7 @@ fn move_file_to_trash() -> anyhow::Result<()> {
|
||||
trash_path.join("foo.py").as_std_path(),
|
||||
)?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -449,7 +502,7 @@ fn move_file_to_workspace() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::rename(foo_path.as_std_path(), foo_in_workspace_path.as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -477,7 +530,7 @@ fn rename_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::rename(foo_path.as_std_path(), bar_path.as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("bar.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -521,7 +574,7 @@ fn directory_moved_to_workspace() -> anyhow::Result<()> {
|
||||
std::fs::rename(sub_original_path.as_std_path(), sub_new_path.as_std_path())
|
||||
.with_context(|| "Failed to move sub directory")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -580,7 +633,7 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
|
||||
std::fs::rename(sub_path.as_std_path(), trashed_sub.as_std_path())
|
||||
.with_context(|| "Failed to move the sub directory to the trash")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -604,8 +657,6 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
|
||||
|
||||
#[test]
|
||||
fn directory_renamed() -> anyhow::Result<()> {
|
||||
let _tracing = setup_logging_with_filter("file_watching=TRACE,red_knot=TRACE");
|
||||
|
||||
let mut case = setup([
|
||||
("bar.py", "import sub.a"),
|
||||
("sub/__init__.py", ""),
|
||||
@@ -644,11 +695,8 @@ fn directory_renamed() -> anyhow::Result<()> {
|
||||
std::fs::rename(sub_path.as_std_path(), foo_baz.as_std_path())
|
||||
.with_context(|| "Failed to move the sub directory")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
|
||||
for event in &changes {
|
||||
tracing::debug!("Event: {:?}", event);
|
||||
}
|
||||
// Linux and windows only emit an event for the newly created root directory, but not for every new component.
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -721,7 +769,7 @@ fn directory_deleted() -> anyhow::Result<()> {
|
||||
std::fs::remove_dir_all(sub_path.as_std_path())
|
||||
.with_context(|| "Failed to remove the sub directory")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -758,7 +806,7 @@ fn search_path() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("a.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -789,7 +837,7 @@ fn add_search_path() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("a.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -818,9 +866,9 @@ fn remove_search_path() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
|
||||
|
||||
let changes = case.try_stop_watch(Duration::from_millis(100));
|
||||
let changes = case.try_stop_watch(|_: &ChangeEvent| true, Duration::from_millis(100));
|
||||
|
||||
assert_eq!(changes, None);
|
||||
assert_eq!(changes, Err(vec![]));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -858,7 +906,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
|
||||
"os: 3.0-",
|
||||
)?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("VERSIONS"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -911,7 +959,7 @@ fn hard_links_in_workspace() -> anyhow::Result<()> {
|
||||
// Write to the hard link target.
|
||||
update_file(foo_path, "print('Version 2')").context("Failed to update foo.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -982,7 +1030,7 @@ fn hard_links_to_target_outside_workspace() -> anyhow::Result<()> {
|
||||
// Write to the hard link target.
|
||||
update_file(foo_path, "print('Version 2')").context("Failed to update foo.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(ChangeEvent::is_changed);
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1021,7 +1069,7 @@ mod unix {
|
||||
)
|
||||
.with_context(|| "Failed to set file permissions.")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1119,7 +1167,7 @@ mod unix {
|
||||
update_file(baz_workspace, "def baz(): print('Version 3')")
|
||||
.context("Failed to update bar/baz.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("baz.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1190,7 +1238,7 @@ mod unix {
|
||||
update_file(&patched_bar_baz, "def baz(): print('Version 2')")
|
||||
.context("Failed to update bar/baz.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("baz.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1298,7 +1346,7 @@ mod unix {
|
||||
update_file(&baz_original, "def baz(): print('Version 2')")
|
||||
.context("Failed to update bar/baz.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("baz.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1352,7 +1400,7 @@ fn nested_packages_delete_root() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::remove_file(case.workspace_path("pyproject.toml").as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(ChangeEvent::is_deleted);
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1364,7 +1412,6 @@ fn nested_packages_delete_root() -> anyhow::Result<()> {
|
||||
|
||||
#[test]
|
||||
fn added_package() -> anyhow::Result<()> {
|
||||
let _ = setup_logging();
|
||||
let mut case = setup([
|
||||
(
|
||||
"pyproject.toml",
|
||||
@@ -1406,7 +1453,7 @@ fn added_package() -> anyhow::Result<()> {
|
||||
)
|
||||
.context("failed to write pyproject.toml for package b")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("pyproject.toml"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1449,7 +1496,7 @@ fn removed_package() -> anyhow::Result<()> {
|
||||
std::fs::remove_dir_all(case.workspace_path("packages/b").as_std_path())
|
||||
.context("failed to remove package 'b'")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(ChangeEvent::is_deleted);
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ anyhow = { workspace = true }
|
||||
dir-test = { workspace = true }
|
||||
insta = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
quickcheck = { version = "1.0.3", default-features = false }
|
||||
quickcheck_macros = { version = "1.0.0" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Any
|
||||
|
||||
## Annotation
|
||||
|
||||
`typing.Any` is a way to name the Any type.
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
|
||||
x: Any = 1
|
||||
x = "foo"
|
||||
|
||||
def f():
|
||||
reveal_type(x) # revealed: Any
|
||||
```
|
||||
|
||||
## Aliased to a different name
|
||||
|
||||
If you alias `typing.Any` to another name, we still recognize that as a spelling of the Any type.
|
||||
|
||||
```py
|
||||
from typing import Any as RenamedAny
|
||||
|
||||
x: RenamedAny = 1
|
||||
x = "foo"
|
||||
|
||||
def f():
|
||||
reveal_type(x) # revealed: Any
|
||||
```
|
||||
|
||||
## Shadowed class
|
||||
|
||||
If you define your own class named `Any`, using that in a type expression refers to your class, and
|
||||
isn't a spelling of the Any type.
|
||||
|
||||
```py
|
||||
class Any:
|
||||
pass
|
||||
|
||||
x: Any
|
||||
|
||||
def f():
|
||||
reveal_type(x) # revealed: Any
|
||||
|
||||
# This verifies that we're not accidentally seeing typing.Any, since str is assignable
|
||||
# to that but not to our locally defined class.
|
||||
y: Any = "not an Any" # error: [invalid-assignment]
|
||||
```
|
||||
|
||||
## Subclass
|
||||
|
||||
The spec allows you to define subclasses of `Any`.
|
||||
|
||||
TODO: Handle assignments correctly. `Subclass` has an unknown superclass, which might be `int`. The
|
||||
assignment to `x` should not be allowed, even when the unknown superclass is `int`. The assignment
|
||||
to `y` should be allowed, since `Subclass` might have `int` as a superclass, and is therefore
|
||||
assignable to `int`.
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
|
||||
class Subclass(Any):
|
||||
pass
|
||||
|
||||
reveal_type(Subclass.__mro__) # revealed: tuple[Literal[Subclass], Any, Literal[object]]
|
||||
|
||||
x: Subclass = 1 # error: [invalid-assignment]
|
||||
# TODO: no diagnostic
|
||||
y: int = Subclass() # error: [invalid-assignment]
|
||||
|
||||
def f() -> Subclass:
|
||||
pass
|
||||
|
||||
reveal_type(f()) # revealed: Subclass
|
||||
```
|
||||
@@ -0,0 +1,128 @@
|
||||
# `LiteralString`
|
||||
|
||||
`LiteralString` represents a string that is either defined directly within the source code or is
|
||||
made up of such components.
|
||||
|
||||
Parts of the testcases defined here were adapted from [the specification's examples][1].
|
||||
|
||||
## Usages
|
||||
|
||||
### Valid places
|
||||
|
||||
It can be used anywhere a type is accepted:
|
||||
|
||||
```py
|
||||
from typing import LiteralString
|
||||
|
||||
x: LiteralString
|
||||
|
||||
def f():
|
||||
reveal_type(x) # revealed: LiteralString
|
||||
```
|
||||
|
||||
### Within `Literal`
|
||||
|
||||
`LiteralString` cannot be used within `Literal`:
|
||||
|
||||
```py
|
||||
from typing import Literal, LiteralString
|
||||
|
||||
bad_union: Literal["hello", LiteralString] # error: [invalid-literal-parameter]
|
||||
bad_nesting: Literal[LiteralString] # error: [invalid-literal-parameter]
|
||||
```
|
||||
|
||||
### Parametrized
|
||||
|
||||
`LiteralString` cannot be parametrized.
|
||||
|
||||
```py
|
||||
from typing import LiteralString
|
||||
|
||||
a: LiteralString[str] # error: [invalid-type-parameter]
|
||||
b: LiteralString["foo"] # error: [invalid-type-parameter]
|
||||
```
|
||||
|
||||
### As a base class
|
||||
|
||||
Subclassing `LiteralString` leads to a runtime error.
|
||||
|
||||
```py
|
||||
from typing import LiteralString
|
||||
|
||||
class C(LiteralString): ... # error: [invalid-base]
|
||||
```
|
||||
|
||||
## Inference
|
||||
|
||||
### Common operations
|
||||
|
||||
```py
|
||||
foo: LiteralString = "foo"
|
||||
reveal_type(foo) # revealed: Literal["foo"]
|
||||
|
||||
bar: LiteralString = "bar"
|
||||
reveal_type(foo + bar) # revealed: Literal["foobar"]
|
||||
|
||||
baz: LiteralString = "baz"
|
||||
baz += foo
|
||||
reveal_type(baz) # revealed: Literal["bazfoo"]
|
||||
|
||||
qux = (foo, bar)
|
||||
reveal_type(qux) # revealed: tuple[Literal["foo"], Literal["bar"]]
|
||||
|
||||
# TODO: Infer "LiteralString"
|
||||
reveal_type(foo.join(qux)) # revealed: @Todo(call todo)
|
||||
|
||||
template: LiteralString = "{}, {}"
|
||||
reveal_type(template) # revealed: Literal["{}, {}"]
|
||||
# TODO: Infer `LiteralString`
|
||||
reveal_type(template.format(foo, bar)) # revealed: @Todo(call todo)
|
||||
```
|
||||
|
||||
### Assignability
|
||||
|
||||
`Literal[""]` is assignable to `LiteralString`, and `LiteralString` is assignable to `str`, but not
|
||||
vice versa.
|
||||
|
||||
```py
|
||||
def coinflip() -> bool:
|
||||
return True
|
||||
|
||||
foo_1: Literal["foo"] = "foo"
|
||||
bar_1: LiteralString = foo_1 # fine
|
||||
|
||||
foo_2 = "foo" if coinflip() else "bar"
|
||||
reveal_type(foo_2) # revealed: Literal["foo", "bar"]
|
||||
bar_2: LiteralString = foo_2 # fine
|
||||
|
||||
foo_3: LiteralString = "foo" * 1_000_000_000
|
||||
bar_3: str = foo_2 # fine
|
||||
|
||||
baz_1: str = str()
|
||||
qux_1: LiteralString = baz_1 # error: [invalid-assignment]
|
||||
|
||||
baz_2: LiteralString = "baz" * 1_000_000_000
|
||||
qux_2: Literal["qux"] = baz_2 # error: [invalid-assignment]
|
||||
|
||||
baz_3 = "foo" if coinflip() else 1
|
||||
reveal_type(baz_3) # revealed: Literal["foo"] | Literal[1]
|
||||
qux_3: LiteralString = baz_3 # error: [invalid-assignment]
|
||||
```
|
||||
|
||||
### Narrowing
|
||||
|
||||
```py
|
||||
lorem: LiteralString = "lorem" * 1_000_000_000
|
||||
|
||||
reveal_type(lorem) # revealed: LiteralString
|
||||
|
||||
if lorem == "ipsum":
|
||||
reveal_type(lorem) # revealed: Literal["ipsum"]
|
||||
|
||||
reveal_type(lorem) # revealed: LiteralString
|
||||
|
||||
if "" < lorem == "ipsum":
|
||||
reveal_type(lorem) # revealed: Literal["ipsum"]
|
||||
```
|
||||
|
||||
[1]: https://typing.readthedocs.io/en/latest/spec/literal.html#literalstring
|
||||
@@ -38,7 +38,7 @@ if (x := 1) and bool_instance():
|
||||
if True or (x := 1):
|
||||
# TODO: infer that the second arm is never executed, and raise `unresolved-reference`.
|
||||
# error: [possibly-unresolved-reference]
|
||||
reveal_type(x) # revealed: Never
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
|
||||
if True and (x := 1):
|
||||
# TODO: infer that the second arm is always executed, do not raise a diagnostic
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# Length (`len()`)
|
||||
|
||||
## Literal and constructed iterables
|
||||
|
||||
### Strings and bytes literals
|
||||
|
||||
```py
|
||||
reveal_type(len("no\rmal")) # revealed: Literal[6]
|
||||
reveal_type(len(r"aw stri\ng")) # revealed: Literal[10]
|
||||
reveal_type(len(r"conca\t" "ena\tion")) # revealed: Literal[14]
|
||||
reveal_type(len(b"ytes lite" rb"al")) # revealed: Literal[11]
|
||||
reveal_type(len("𝒰𝕹🄸©🕲𝕕ℇ")) # revealed: Literal[7]
|
||||
|
||||
reveal_type( # revealed: Literal[7]
|
||||
len(
|
||||
"""foo
|
||||
bar"""
|
||||
)
|
||||
)
|
||||
reveal_type( # revealed: Literal[9]
|
||||
len(
|
||||
r"""foo\r
|
||||
bar"""
|
||||
)
|
||||
)
|
||||
reveal_type( # revealed: Literal[7]
|
||||
len(
|
||||
b"""foo
|
||||
bar"""
|
||||
)
|
||||
)
|
||||
reveal_type( # revealed: Literal[9]
|
||||
len(
|
||||
rb"""foo\r
|
||||
bar"""
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
### Tuples
|
||||
|
||||
```py
|
||||
reveal_type(len(())) # revealed: Literal[0]
|
||||
reveal_type(len((1,))) # revealed: Literal[1]
|
||||
reveal_type(len((1, 2))) # revealed: Literal[2]
|
||||
|
||||
# TODO: Handle constructor calls
|
||||
reveal_type(len(tuple())) # revealed: int
|
||||
|
||||
# TODO: Handle star unpacks; Should be: Literal[0]
|
||||
reveal_type(len((*[],))) # revealed: Literal[1]
|
||||
|
||||
# TODO: Handle star unpacks; Should be: Literal[1]
|
||||
reveal_type( # revealed: Literal[2]
|
||||
len(
|
||||
(
|
||||
*[],
|
||||
1,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: Handle star unpacks; Should be: Literal[2]
|
||||
reveal_type(len((*[], 1, 2))) # revealed: Literal[3]
|
||||
|
||||
# TODO: Handle star unpacks; Should be: Literal[0]
|
||||
reveal_type(len((*[], *{}))) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
### Lists, sets and dictionaries
|
||||
|
||||
```py
|
||||
reveal_type(len([])) # revealed: int
|
||||
reveal_type(len([1])) # revealed: int
|
||||
reveal_type(len([1, 2])) # revealed: int
|
||||
reveal_type(len([*{}, *dict()])) # revealed: int
|
||||
|
||||
reveal_type(len({})) # revealed: int
|
||||
reveal_type(len({**{}})) # revealed: int
|
||||
reveal_type(len({**{}, **{}})) # revealed: int
|
||||
|
||||
reveal_type(len({1})) # revealed: int
|
||||
reveal_type(len({1, 2})) # revealed: int
|
||||
reveal_type(len({*[], 2})) # revealed: int
|
||||
|
||||
reveal_type(len(list())) # revealed: int
|
||||
reveal_type(len(set())) # revealed: int
|
||||
reveal_type(len(dict())) # revealed: int
|
||||
reveal_type(len(frozenset())) # revealed: int
|
||||
```
|
||||
|
||||
## `__len__`
|
||||
|
||||
The returned value of `__len__` is implicitly and recursively converted to `int`.
|
||||
|
||||
### Literal integers
|
||||
|
||||
```py
|
||||
from typing import Literal
|
||||
|
||||
class Zero:
|
||||
def __len__(self) -> Literal[0]: ...
|
||||
|
||||
class ZeroOrOne:
|
||||
def __len__(self) -> Literal[0, 1]: ...
|
||||
|
||||
class ZeroOrTrue:
|
||||
def __len__(self) -> Literal[0, True]: ...
|
||||
|
||||
class OneOrFalse:
|
||||
def __len__(self) -> Literal[1] | Literal[False]: ...
|
||||
|
||||
class OneOrFoo:
|
||||
def __len__(self) -> Literal[1, "foo"]: ...
|
||||
|
||||
class ZeroOrStr:
|
||||
def __len__(self) -> Literal[0] | str: ...
|
||||
|
||||
reveal_type(len(Zero())) # revealed: Literal[0]
|
||||
reveal_type(len(ZeroOrOne())) # revealed: Literal[0, 1]
|
||||
reveal_type(len(ZeroOrTrue())) # revealed: Literal[0, 1]
|
||||
reveal_type(len(OneOrFalse())) # revealed: Literal[0, 1]
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
reveal_type(len(OneOrFoo())) # revealed: int
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
reveal_type(len(ZeroOrStr())) # revealed: int
|
||||
```
|
||||
|
||||
### Literal booleans
|
||||
|
||||
```py
|
||||
from typing import Literal
|
||||
|
||||
class LiteralTrue:
|
||||
def __len__(self) -> Literal[True]: ...
|
||||
|
||||
class LiteralFalse:
|
||||
def __len__(self) -> Literal[False]: ...
|
||||
|
||||
reveal_type(len(LiteralTrue())) # revealed: Literal[1]
|
||||
reveal_type(len(LiteralFalse())) # revealed: Literal[0]
|
||||
```
|
||||
|
||||
### Enums
|
||||
|
||||
```py
|
||||
from enum import Enum, auto
|
||||
from typing import Literal
|
||||
|
||||
class SomeEnum(Enum):
|
||||
AUTO = auto()
|
||||
INT = 2
|
||||
STR = "4"
|
||||
TUPLE = (8, "16")
|
||||
INT_2 = 3_2
|
||||
|
||||
class Auto:
|
||||
def __len__(self) -> Literal[SomeEnum.AUTO]: ...
|
||||
|
||||
class Int:
|
||||
def __len__(self) -> Literal[SomeEnum.INT]: ...
|
||||
|
||||
class Str:
|
||||
def __len__(self) -> Literal[SomeEnum.STR]: ...
|
||||
|
||||
class Tuple:
|
||||
def __len__(self) -> Literal[SomeEnum.TUPLE]: ...
|
||||
|
||||
class IntUnion:
|
||||
def __len__(self) -> Literal[SomeEnum.INT, SomeEnum.INT_2]: ...
|
||||
|
||||
reveal_type(len(Auto())) # revealed: int
|
||||
reveal_type(len(Int())) # revealed: Literal[2]
|
||||
reveal_type(len(Str())) # revealed: int
|
||||
reveal_type(len(Tuple())) # revealed: int
|
||||
reveal_type(len(IntUnion())) # revealed: Literal[2, 32]
|
||||
```
|
||||
|
||||
### Negative integers
|
||||
|
||||
```py
|
||||
from typing import Literal
|
||||
|
||||
class Negative:
|
||||
def __len__(self) -> Literal[-1]: ...
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
reveal_type(len(Negative())) # revealed: int
|
||||
```
|
||||
|
||||
### Wrong signature
|
||||
|
||||
```py
|
||||
from typing import Literal
|
||||
|
||||
class SecondOptionalArgument:
|
||||
def __len__(self, v: int = 0) -> Literal[0]: ...
|
||||
|
||||
class SecondRequiredArgument:
|
||||
def __len__(self, v: int) -> Literal[1]: ...
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
reveal_type(len(SecondOptionalArgument())) # revealed: Literal[0]
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
reveal_type(len(SecondRequiredArgument())) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
### No `__len__`
|
||||
|
||||
```py
|
||||
class NoDunderLen:
|
||||
pass
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
reveal_type(len(NoDunderLen())) # revealed: int
|
||||
```
|
||||
@@ -0,0 +1,64 @@
|
||||
# Syntax errors
|
||||
|
||||
Test cases to ensure that red knot does not panic if there are syntax errors in the source code.
|
||||
|
||||
## Keyword as identifiers
|
||||
|
||||
When keywords are used as identifiers, the parser recovers from this syntax error by emitting an
|
||||
error and including the text value of the keyword to create the `Identifier` node.
|
||||
|
||||
### Name expression
|
||||
|
||||
```py
|
||||
# error: [invalid-syntax]
|
||||
pass = 1
|
||||
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
type pass = 1
|
||||
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
def True(for):
|
||||
# error: [invalid-syntax]
|
||||
pass
|
||||
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [unresolved-reference] "Name `pass` used when not defined"
|
||||
for while in pass:
|
||||
pass
|
||||
|
||||
# error: [invalid-syntax]
|
||||
# error: [unresolved-reference] "Name `in` used when not defined"
|
||||
while in:
|
||||
pass
|
||||
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [unresolved-reference] "Name `match` used when not defined"
|
||||
match while:
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
# error: [unresolved-reference] "Name `case` used when not defined"
|
||||
case in:
|
||||
# error: [invalid-syntax]
|
||||
# error: [invalid-syntax]
|
||||
pass
|
||||
```
|
||||
|
||||
### Attribute expression
|
||||
|
||||
```py
|
||||
# TODO: Check when support for attribute expressions is added
|
||||
|
||||
# error: [invalid-syntax]
|
||||
# error: [unresolved-reference] "Name `foo` used when not defined"
|
||||
for x in foo.pass:
|
||||
pass
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
## Narrowing for `bool(..)` checks
|
||||
|
||||
```py
|
||||
def flag() -> bool: ...
|
||||
|
||||
x = 1 if flag() else None
|
||||
|
||||
# valid invocation, positive
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if bool(x is not None):
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
|
||||
# valid invocation, negative
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if not bool(x is not None):
|
||||
reveal_type(x) # revealed: None
|
||||
|
||||
# no args/narrowing
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if not bool():
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
|
||||
# invalid invocation, too many positional args
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if bool(x is not None, 5): # TODO diagnostic
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
|
||||
# invalid invocation, too many kwargs
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if bool(x is not None, y=5): # TODO diagnostic
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
```
|
||||
@@ -1,303 +0,0 @@
|
||||
# Statically-known branches
|
||||
|
||||
## Always false
|
||||
|
||||
### If
|
||||
|
||||
```py
|
||||
x = 1
|
||||
|
||||
if False:
|
||||
x = 2
|
||||
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
### Else
|
||||
|
||||
```py
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
pass
|
||||
else:
|
||||
x = 2
|
||||
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
## Always true
|
||||
|
||||
### If
|
||||
|
||||
```py
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
x = 2
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
### Else
|
||||
|
||||
```py
|
||||
x = 1
|
||||
|
||||
if False:
|
||||
pass
|
||||
else:
|
||||
x = 2
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
## Combination
|
||||
|
||||
```py
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
x = 2
|
||||
else:
|
||||
x = 3
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
## Nested
|
||||
|
||||
```py path=nested_if_true_if_true.py
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
if True:
|
||||
x = 2
|
||||
else:
|
||||
x = 3
|
||||
else:
|
||||
x = 4
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
```py path=nested_if_true_if_false.py
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
if False:
|
||||
x = 2
|
||||
else:
|
||||
x = 3
|
||||
else:
|
||||
x = 4
|
||||
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
```
|
||||
|
||||
```py path=nested_if_true_if_bool.py
|
||||
def flag() -> bool: ...
|
||||
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
if flag():
|
||||
x = 2
|
||||
else:
|
||||
x = 3
|
||||
else:
|
||||
x = 4
|
||||
|
||||
reveal_type(x) # revealed: Literal[2, 3]
|
||||
```
|
||||
|
||||
```py path=nested_if_bool_if_true.py
|
||||
def flag() -> bool: ...
|
||||
|
||||
x = 1
|
||||
|
||||
if flag():
|
||||
if True:
|
||||
x = 2
|
||||
else:
|
||||
x = 3
|
||||
else:
|
||||
x = 4
|
||||
|
||||
reveal_type(x) # revealed: Literal[2, 4]
|
||||
```
|
||||
|
||||
```py path=nested_else_if_true.py
|
||||
x = 1
|
||||
|
||||
if False:
|
||||
x = 2
|
||||
else:
|
||||
if True:
|
||||
x = 3
|
||||
else:
|
||||
x = 4
|
||||
|
||||
reveal_type(x) # revealed: Literal[3]
|
||||
```
|
||||
|
||||
```py path=nested_else_if_false.py
|
||||
x = 1
|
||||
|
||||
if False:
|
||||
x = 2
|
||||
else:
|
||||
if False:
|
||||
x = 3
|
||||
else:
|
||||
x = 4
|
||||
|
||||
reveal_type(x) # revealed: Literal[4]
|
||||
```
|
||||
|
||||
```py path=nested_else_if_bool.py
|
||||
def flag() -> bool: ...
|
||||
|
||||
x = 1
|
||||
|
||||
if False:
|
||||
x = 2
|
||||
else:
|
||||
if flag():
|
||||
x = 3
|
||||
else:
|
||||
x = 4
|
||||
|
||||
reveal_type(x) # revealed: Literal[3, 4]
|
||||
```
|
||||
|
||||
## If-expressions
|
||||
|
||||
### Always true
|
||||
|
||||
```py
|
||||
x = 1 if True else 2
|
||||
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
### Always false
|
||||
|
||||
```py
|
||||
x = 1 if False else 2
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
## Boolean expressions
|
||||
|
||||
### Always true
|
||||
|
||||
```py
|
||||
(x := 1) == 1 or (x := 2)
|
||||
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
```
|
||||
|
||||
### Always false
|
||||
|
||||
```py
|
||||
(x := 1) == 0 or (x := 2)
|
||||
|
||||
reveal_type(x) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
## Conditional declarations
|
||||
|
||||
```py path=if_false.py
|
||||
x: str
|
||||
|
||||
if False:
|
||||
x: int
|
||||
|
||||
def f() -> None:
|
||||
reveal_type(x) # revealed: str
|
||||
```
|
||||
|
||||
```py path=if_true_else.py
|
||||
x: str
|
||||
|
||||
if True:
|
||||
pass
|
||||
else:
|
||||
x: int
|
||||
|
||||
def f() -> None:
|
||||
reveal_type(x) # revealed: str
|
||||
```
|
||||
|
||||
```py path=if_true.py
|
||||
x: str
|
||||
|
||||
if True:
|
||||
x: int
|
||||
|
||||
def f() -> None:
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
```py path=if_false_else.py
|
||||
x: str
|
||||
|
||||
if False:
|
||||
pass
|
||||
else:
|
||||
x: int
|
||||
|
||||
def f() -> None:
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
```py path=if_bool.py
|
||||
def flag() -> bool: ...
|
||||
|
||||
x: str
|
||||
|
||||
if flag():
|
||||
x: int
|
||||
|
||||
def f() -> None:
|
||||
reveal_type(x) # revealed: str | int
|
||||
```
|
||||
|
||||
## Conditionally defined functions
|
||||
|
||||
```py
|
||||
def f() -> int: ...
|
||||
def g() -> int: ...
|
||||
|
||||
if True:
|
||||
def f() -> str: ...
|
||||
|
||||
else:
|
||||
def g() -> str: ...
|
||||
|
||||
reveal_type(f()) # revealed: str
|
||||
reveal_type(g()) # revealed: int
|
||||
```
|
||||
|
||||
## Conditionally defined class attributes
|
||||
|
||||
```py
|
||||
class C:
|
||||
if True:
|
||||
x: int = 1
|
||||
else:
|
||||
x: str = "a"
|
||||
|
||||
reveal_type(C.x) # revealed: int
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- declarations vs bindings => NoDefault: NoDefaultType
|
||||
- conditional imports
|
||||
- conditional class definitions
|
||||
- compare with tests in if.md=>Statically known branches
|
||||
- boundness
|
||||
- TODO in `issubclass.md`
|
||||
@@ -49,14 +49,14 @@ sometimes not:
|
||||
```py
|
||||
import sys
|
||||
|
||||
reveal_type(sys.version_info >= (3, 9, 1)) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info >= (3, 9, 1, "final", 0)) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info >= (3, 9, 1)) # revealed: bool
|
||||
reveal_type(sys.version_info >= (3, 9, 1, "final", 0)) # revealed: bool
|
||||
|
||||
# TODO: While this won't fail at runtime, the user has probably made a mistake
|
||||
# if they're comparing a tuple of length >5 with `sys.version_info`
|
||||
# (`sys.version_info` is a tuple of length 5). It might be worth
|
||||
# emitting a lint diagnostic of some kind warning them about the probable error?
|
||||
reveal_type(sys.version_info >= (3, 9, 1, "final", 0, 5)) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info >= (3, 9, 1, "final", 0, 5)) # revealed: bool
|
||||
|
||||
reveal_type(sys.version_info == (3, 8, 1, "finallllll", 0)) # revealed: Literal[False]
|
||||
```
|
||||
@@ -102,8 +102,8 @@ The fields of `sys.version_info` can be accessed by name:
|
||||
import sys
|
||||
|
||||
reveal_type(sys.version_info.major >= 3) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info.minor >= 13) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info.minor >= 14) # revealed: Literal[False]
|
||||
reveal_type(sys.version_info.minor >= 9) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info.minor >= 10) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
But the `micro`, `releaselevel` and `serial` fields are inferred as `@Todo` until we support
|
||||
@@ -125,14 +125,14 @@ The fields of `sys.version_info` can be accessed by index or by slice:
|
||||
import sys
|
||||
|
||||
reveal_type(sys.version_info[0] < 3) # revealed: Literal[False]
|
||||
reveal_type(sys.version_info[1] > 13) # revealed: Literal[False]
|
||||
reveal_type(sys.version_info[1] > 9) # revealed: Literal[False]
|
||||
|
||||
# revealed: tuple[Literal[3], Literal[13], int, Literal["alpha", "beta", "candidate", "final"], int]
|
||||
# revealed: tuple[Literal[3], Literal[9], int, Literal["alpha", "beta", "candidate", "final"], int]
|
||||
reveal_type(sys.version_info[:5])
|
||||
|
||||
reveal_type(sys.version_info[:2] >= (3, 13)) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info[0:2] >= (3, 14)) # revealed: Literal[False]
|
||||
reveal_type(sys.version_info[:3] >= (3, 14, 1)) # revealed: Literal[False]
|
||||
reveal_type(sys.version_info[:2] >= (3, 9)) # revealed: Literal[True]
|
||||
reveal_type(sys.version_info[0:2] >= (3, 10)) # revealed: Literal[False]
|
||||
reveal_type(sys.version_info[:3] >= (3, 10, 1)) # revealed: Literal[False]
|
||||
reveal_type(sys.version_info[3] == "final") # revealed: bool
|
||||
reveal_type(sys.version_info[3] == "finalllllll") # revealed: Literal[False]
|
||||
```
|
||||
|
||||
@@ -267,3 +267,42 @@ reveal_type(b) # revealed: LiteralString
|
||||
# TODO: Should be list[int] once support for assigning to starred expression is added
|
||||
reveal_type(c) # revealed: @Todo(starred unpacking)
|
||||
```
|
||||
|
||||
### Unicode
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (need more values to unpack)
|
||||
(a, b) = "é"
|
||||
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Unicode escape (1)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (need more values to unpack)
|
||||
(a, b) = "\u9E6C"
|
||||
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Unicode escape (2)
|
||||
|
||||
```py
|
||||
# TODO: Add diagnostic (need more values to unpack)
|
||||
(a, b) = "\U0010FFFF"
|
||||
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Surrogates
|
||||
|
||||
```py
|
||||
(a, b) = "\uD800\uDFFF"
|
||||
|
||||
reveal_type(a) # revealed: LiteralString
|
||||
reveal_type(b) # revealed: LiteralString
|
||||
```
|
||||
|
||||
@@ -11,8 +11,13 @@ pub trait Db: SourceDb + Upcast<dyn SourceDb> {
|
||||
pub(crate) mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::ProgramSettings;
|
||||
|
||||
use anyhow::Context;
|
||||
use ruff_db::files::{File, Files};
|
||||
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
|
||||
use ruff_db::system::{DbWithTestSystem, System, SystemPathBuf, TestSystem};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_db::{Db as SourceDb, Upcast};
|
||||
|
||||
@@ -108,4 +113,66 @@ pub(crate) mod tests {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TestDbBuilder<'a> {
|
||||
/// Target Python version
|
||||
python_version: PythonVersion,
|
||||
/// Path to a custom typeshed directory
|
||||
custom_typeshed: Option<SystemPathBuf>,
|
||||
/// Path and content pairs for files that should be present
|
||||
files: Vec<(&'a str, &'a str)>,
|
||||
}
|
||||
|
||||
impl<'a> TestDbBuilder<'a> {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
python_version: PythonVersion::default(),
|
||||
custom_typeshed: None,
|
||||
files: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_python_version(mut self, version: PythonVersion) -> Self {
|
||||
self.python_version = version;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_custom_typeshed(mut self, path: &str) -> Self {
|
||||
self.custom_typeshed = Some(SystemPathBuf::from(path));
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_file(mut self, path: &'a str, content: &'a str) -> Self {
|
||||
self.files.push((path, content));
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn build(self) -> anyhow::Result<TestDb> {
|
||||
let mut db = TestDb::new();
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system().create_directory_all(&src_root)?;
|
||||
|
||||
db.write_files(self.files)
|
||||
.context("Failed to write test files")?;
|
||||
|
||||
let mut search_paths = SearchPathSettings::new(src_root);
|
||||
search_paths.custom_typeshed = self.custom_typeshed;
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: self.python_version,
|
||||
search_paths,
|
||||
},
|
||||
)
|
||||
.context("Failed to configure Program settings")?;
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn setup_db() -> TestDb {
|
||||
TestDbBuilder::new().build().expect("valid TestDb setup")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ impl<'db> Iterator for SearchPathIterator<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> FusedIterator for SearchPathIterator<'db> {}
|
||||
impl FusedIterator for SearchPathIterator<'_> {}
|
||||
|
||||
/// Represents a single `.pth` file in a `site-packages` directory.
|
||||
/// One or more lines in a `.pth` file may be a (relative or absolute)
|
||||
|
||||
@@ -39,7 +39,7 @@ impl PythonVersion {
|
||||
|
||||
impl Default for PythonVersion {
|
||||
fn default() -> Self {
|
||||
Self::PY313 // TODO: temporarily changed to 3.13 to activate all sys.version_info branches
|
||||
Self::PY39
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1229,32 +1229,4 @@ match 1:
|
||||
|
||||
assert!(matches!(binding.kind(&db), DefinitionKind::For(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn if_statement() {
|
||||
let TestCase { db, file } = test_case(
|
||||
"
|
||||
x = False
|
||||
|
||||
if True:
|
||||
x: bool
|
||||
",
|
||||
);
|
||||
|
||||
let index = semantic_index(&db, file);
|
||||
// let global_table = index.symbol_table(FileScopeId::global());
|
||||
|
||||
let use_def = index.use_def_map(FileScopeId::global());
|
||||
|
||||
// use_def
|
||||
|
||||
use_def.print(&db);
|
||||
|
||||
assert!(false);
|
||||
// let binding = use_def
|
||||
// .first_public_binding(global_table.symbol_id_by_name(name).expect("symbol exists"))
|
||||
// .expect("Expected with item definition for {name}");
|
||||
// assert!(matches!(binding.kind(&db), DefinitionKind::WithItem(_)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::semantic_index::symbol::{
|
||||
FileScopeId, NodeWithScopeKey, NodeWithScopeRef, Scope, ScopeId, ScopedSymbolId,
|
||||
SymbolTableBuilder,
|
||||
};
|
||||
use crate::semantic_index::use_def::{ActiveConstraintsSnapshot, FlowSnapshot, UseDefMapBuilder};
|
||||
use crate::semantic_index::use_def::{FlowSnapshot, UseDefMapBuilder};
|
||||
use crate::semantic_index::SemanticIndex;
|
||||
use crate::unpack::Unpack;
|
||||
use crate::Db;
|
||||
@@ -200,20 +200,12 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
self.current_use_def_map().snapshot()
|
||||
}
|
||||
|
||||
fn constraints_snapshot(&self) -> ActiveConstraintsSnapshot {
|
||||
self.current_use_def_map().constraints_snapshot()
|
||||
}
|
||||
|
||||
fn flow_restore(&mut self, state: FlowSnapshot, active_constraints: ActiveConstraintsSnapshot) {
|
||||
fn flow_restore(&mut self, state: FlowSnapshot) {
|
||||
self.current_use_def_map_mut().restore(state);
|
||||
self.current_use_def_map_mut()
|
||||
.restore_constraints(active_constraints);
|
||||
}
|
||||
|
||||
fn flow_merge(&mut self, state: FlowSnapshot, active_constraints: ActiveConstraintsSnapshot) {
|
||||
fn flow_merge(&mut self, state: FlowSnapshot) {
|
||||
self.current_use_def_map_mut().merge(state);
|
||||
self.current_use_def_map_mut()
|
||||
.restore_constraints(active_constraints);
|
||||
}
|
||||
|
||||
fn add_symbol(&mut self, name: Name) -> ScopedSymbolId {
|
||||
@@ -773,7 +765,6 @@ where
|
||||
ast::Stmt::If(node) => {
|
||||
self.visit_expr(&node.test);
|
||||
let pre_if = self.flow_snapshot();
|
||||
let pre_if_constraints = self.constraints_snapshot();
|
||||
let constraint = self.record_expression_constraint(&node.test);
|
||||
let mut constraints = vec![constraint];
|
||||
self.visit_body(&node.body);
|
||||
@@ -799,7 +790,7 @@ where
|
||||
post_clauses.push(self.flow_snapshot());
|
||||
// we can only take an elif/else branch if none of the previous ones were
|
||||
// taken, so the block entry state is always `pre_if`
|
||||
self.flow_restore(pre_if.clone(), pre_if_constraints.clone());
|
||||
self.flow_restore(pre_if.clone());
|
||||
for constraint in &constraints {
|
||||
self.record_negated_constraint(*constraint);
|
||||
}
|
||||
@@ -810,7 +801,7 @@ where
|
||||
self.visit_body(clause_body);
|
||||
}
|
||||
for post_clause_state in post_clauses {
|
||||
self.flow_merge(post_clause_state, pre_if_constraints.clone());
|
||||
self.flow_merge(post_clause_state);
|
||||
}
|
||||
}
|
||||
ast::Stmt::While(ast::StmtWhile {
|
||||
@@ -822,7 +813,6 @@ where
|
||||
self.visit_expr(test);
|
||||
|
||||
let pre_loop = self.flow_snapshot();
|
||||
let pre_loop_constraints = self.constraints_snapshot();
|
||||
|
||||
// Save aside any break states from an outer loop
|
||||
let saved_break_states = std::mem::take(&mut self.loop_break_states);
|
||||
@@ -841,13 +831,13 @@ where
|
||||
|
||||
// We may execute the `else` clause without ever executing the body, so merge in
|
||||
// the pre-loop state before visiting `else`.
|
||||
self.flow_merge(pre_loop, pre_loop_constraints.clone());
|
||||
self.flow_merge(pre_loop);
|
||||
self.visit_body(orelse);
|
||||
|
||||
// Breaking out of a while loop bypasses the `else` clause, so merge in the break
|
||||
// states after visiting `else`.
|
||||
for break_state in break_states {
|
||||
self.flow_merge(break_state, pre_loop_constraints.clone()); // TODO?
|
||||
self.flow_merge(break_state);
|
||||
}
|
||||
}
|
||||
ast::Stmt::With(ast::StmtWith {
|
||||
@@ -890,7 +880,6 @@ where
|
||||
self.visit_expr(iter);
|
||||
|
||||
let pre_loop = self.flow_snapshot();
|
||||
let pre_loop_constraints = self.constraints_snapshot();
|
||||
let saved_break_states = std::mem::take(&mut self.loop_break_states);
|
||||
|
||||
debug_assert_eq!(&self.current_assignments, &[]);
|
||||
@@ -911,13 +900,13 @@ where
|
||||
|
||||
// We may execute the `else` clause without ever executing the body, so merge in
|
||||
// the pre-loop state before visiting `else`.
|
||||
self.flow_merge(pre_loop, pre_loop_constraints.clone());
|
||||
self.flow_merge(pre_loop);
|
||||
self.visit_body(orelse);
|
||||
|
||||
// Breaking out of a `for` loop bypasses the `else` clause, so merge in the break
|
||||
// states after visiting `else`.
|
||||
for break_state in break_states {
|
||||
self.flow_merge(break_state, pre_loop_constraints.clone());
|
||||
self.flow_merge(break_state);
|
||||
}
|
||||
}
|
||||
ast::Stmt::Match(ast::StmtMatch {
|
||||
@@ -929,7 +918,6 @@ where
|
||||
self.visit_expr(subject);
|
||||
|
||||
let after_subject = self.flow_snapshot();
|
||||
let after_subject_cs = self.constraints_snapshot();
|
||||
let Some((first, remaining)) = cases.split_first() else {
|
||||
return;
|
||||
};
|
||||
@@ -939,18 +927,18 @@ where
|
||||
let mut post_case_snapshots = vec![];
|
||||
for case in remaining {
|
||||
post_case_snapshots.push(self.flow_snapshot());
|
||||
self.flow_restore(after_subject.clone(), after_subject_cs.clone());
|
||||
self.flow_restore(after_subject.clone());
|
||||
self.add_pattern_constraint(subject, &case.pattern);
|
||||
self.visit_match_case(case);
|
||||
}
|
||||
for post_clause_state in post_case_snapshots {
|
||||
self.flow_merge(post_clause_state, after_subject_cs.clone());
|
||||
self.flow_merge(post_clause_state);
|
||||
}
|
||||
if !cases
|
||||
.last()
|
||||
.is_some_and(|case| case.guard.is_none() && case.pattern.is_wildcard())
|
||||
{
|
||||
self.flow_merge(after_subject, after_subject_cs.clone());
|
||||
self.flow_merge(after_subject);
|
||||
}
|
||||
}
|
||||
ast::Stmt::Try(ast::StmtTry {
|
||||
@@ -968,7 +956,6 @@ where
|
||||
// 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();
|
||||
let pre_try_block_constraints = self.constraints_snapshot();
|
||||
|
||||
self.try_node_context_stack_manager.push_context();
|
||||
|
||||
@@ -989,17 +976,14 @@ where
|
||||
// as there necessarily must have been 0 `except` blocks executed
|
||||
// if we hit the `else` block.
|
||||
let post_try_block_state = self.flow_snapshot();
|
||||
let post_try_block_constraints = self.constraints_snapshot();
|
||||
|
||||
// Prepare for visiting the `except` block(s)
|
||||
self.flow_restore(pre_try_block_state, pre_try_block_constraints.clone());
|
||||
self.flow_restore(pre_try_block_state);
|
||||
for state in try_block_snapshots {
|
||||
self.flow_merge(state, pre_try_block_constraints.clone());
|
||||
// TODO?
|
||||
self.flow_merge(state);
|
||||
}
|
||||
|
||||
let pre_except_state = self.flow_snapshot();
|
||||
let pre_except_constraints = self.constraints_snapshot();
|
||||
let num_handlers = handlers.len();
|
||||
|
||||
for (i, except_handler) in handlers.iter().enumerate() {
|
||||
@@ -1038,22 +1022,19 @@ where
|
||||
// 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(),
|
||||
pre_except_constraints.clone(),
|
||||
);
|
||||
self.flow_restore(pre_except_state.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// 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, post_try_block_constraints);
|
||||
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, pre_try_block_constraints.clone());
|
||||
self.flow_merge(post_except_state);
|
||||
}
|
||||
|
||||
// TODO: there's lots of complexity here that isn't yet handled by our model.
|
||||
@@ -1210,17 +1191,19 @@ where
|
||||
ast::Expr::If(ast::ExprIf {
|
||||
body, test, orelse, ..
|
||||
}) => {
|
||||
// TODO detect statically known truthy or falsy test (via type inference, not naive
|
||||
// AST inspection, so we can't simplify here, need to record test expression for
|
||||
// later checking)
|
||||
self.visit_expr(test);
|
||||
let pre_if = self.flow_snapshot();
|
||||
let pre_if_constraints = self.constraints_snapshot();
|
||||
let constraint = self.record_expression_constraint(test);
|
||||
self.visit_expr(body);
|
||||
let post_body = self.flow_snapshot();
|
||||
self.flow_restore(pre_if, pre_if_constraints.clone());
|
||||
self.flow_restore(pre_if);
|
||||
|
||||
self.record_negated_constraint(constraint);
|
||||
self.visit_expr(orelse);
|
||||
self.flow_merge(post_body, pre_if_constraints);
|
||||
self.flow_merge(post_body);
|
||||
}
|
||||
ast::Expr::ListComp(
|
||||
list_comprehension @ ast::ExprListComp {
|
||||
@@ -1281,7 +1264,7 @@ where
|
||||
// AST inspection, so we can't simplify here, need to record test expression for
|
||||
// later checking)
|
||||
let mut snapshots = vec![];
|
||||
let pre_op_constraints = self.constraints_snapshot();
|
||||
|
||||
for (index, value) in values.iter().enumerate() {
|
||||
self.visit_expr(value);
|
||||
// In the last value we don't need to take a snapshot nor add a constraint
|
||||
@@ -1296,7 +1279,7 @@ where
|
||||
}
|
||||
}
|
||||
for snapshot in snapshots {
|
||||
self.flow_merge(snapshot, pre_op_constraints.clone());
|
||||
self.flow_merge(snapshot);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -221,8 +221,6 @@
|
||||
//! snapshot, and merging a snapshot into the current state. The logic using these methods lives in
|
||||
//! [`SemanticIndexBuilder`](crate::semantic_index::builder::SemanticIndexBuilder), e.g. where it
|
||||
//! visits a `StmtIf` node.
|
||||
use std::collections::HashSet;
|
||||
|
||||
use self::symbol_state::{
|
||||
BindingIdWithConstraintsIterator, ConstraintIdIterator, DeclarationIdIterator,
|
||||
ScopedConstraintId, ScopedDefinitionId, SymbolBindings, SymbolDeclarations, SymbolState,
|
||||
@@ -270,109 +268,6 @@ pub(crate) struct UseDefMap<'db> {
|
||||
}
|
||||
|
||||
impl<'db> UseDefMap<'db> {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn print(&self, db: &dyn crate::db::Db) {
|
||||
use crate::semantic_index::constraint::ConstraintNode;
|
||||
|
||||
println!("all_definitions:");
|
||||
println!("================");
|
||||
|
||||
for (id, d) in self.all_definitions.iter_enumerated() {
|
||||
println!(
|
||||
"{:?}: {:?} {:?} {:?}",
|
||||
id,
|
||||
d.category(db),
|
||||
d.scope(db),
|
||||
d.symbol(db),
|
||||
);
|
||||
println!(" {:?}", d.kind(db));
|
||||
println!();
|
||||
}
|
||||
|
||||
println!("all_constraints:");
|
||||
println!("================");
|
||||
|
||||
for (id, c) in self.all_constraints.iter_enumerated() {
|
||||
println!("{:?}: {:?}", id, c.node);
|
||||
if let ConstraintNode::Expression(e) = c.node {
|
||||
println!(" {:?}", e.node_ref(db));
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
println!("bindings_by_use:");
|
||||
println!("================");
|
||||
|
||||
for (id, bindings) in self.bindings_by_use.iter_enumerated() {
|
||||
println!("{:?}:", id);
|
||||
for binding in bindings.iter() {
|
||||
let definition = self.all_definitions[binding.definition];
|
||||
let mut constraint_ids = binding.constraint_ids.peekable();
|
||||
let mut active_constraint_ids =
|
||||
binding.constraints_active_at_binding_ids.peekable();
|
||||
|
||||
println!(" * {:?}", definition);
|
||||
|
||||
if constraint_ids.peek().is_some() {
|
||||
println!(" Constraints:");
|
||||
for constraint_id in constraint_ids {
|
||||
println!(" {:?}", self.all_constraints[constraint_id]);
|
||||
}
|
||||
} else {
|
||||
println!(" No constraints");
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
if active_constraint_ids.peek().is_some() {
|
||||
println!(" Active constraints at binding:");
|
||||
for constraint_id in active_constraint_ids {
|
||||
println!(" {:?}", self.all_constraints[constraint_id]);
|
||||
}
|
||||
} else {
|
||||
println!(" No active constraints at binding");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
println!("public_symbols:");
|
||||
println!("================");
|
||||
|
||||
for (id, symbol) in self.public_symbols.iter_enumerated() {
|
||||
println!("{:?}:", id);
|
||||
println!(" * Bindings:");
|
||||
for binding in symbol.bindings().iter() {
|
||||
let definition = self.all_definitions[binding.definition];
|
||||
let mut constraint_ids = binding.constraint_ids.peekable();
|
||||
|
||||
println!(" {:?}", definition);
|
||||
|
||||
if constraint_ids.peek().is_some() {
|
||||
println!(" Constraints:");
|
||||
for constraint_id in constraint_ids {
|
||||
println!(" {:?}", self.all_constraints[constraint_id]);
|
||||
}
|
||||
} else {
|
||||
println!(" No constraints");
|
||||
}
|
||||
}
|
||||
|
||||
println!(" * Declarations:");
|
||||
for (declaration, _) in symbol.declarations().iter() {
|
||||
let definition = self.all_definitions[declaration];
|
||||
println!(" {:?}", definition);
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
println!();
|
||||
println!();
|
||||
}
|
||||
|
||||
pub(crate) fn bindings_at_use(
|
||||
&self,
|
||||
use_id: ScopedUseId,
|
||||
@@ -457,7 +352,6 @@ impl<'db> UseDefMap<'db> {
|
||||
) -> DeclarationsIterator<'a, 'db> {
|
||||
DeclarationsIterator {
|
||||
all_definitions: &self.all_definitions,
|
||||
all_constraints: &self.all_constraints,
|
||||
inner: declarations.iter(),
|
||||
may_be_undeclared: declarations.may_be_undeclared(),
|
||||
}
|
||||
@@ -471,7 +365,7 @@ enum SymbolDefinitions {
|
||||
Declarations(SymbolDeclarations),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct BindingWithConstraintsIterator<'map, 'db> {
|
||||
all_definitions: &'map IndexVec<ScopedDefinitionId, Definition<'db>>,
|
||||
all_constraints: &'map IndexVec<ScopedConstraintId, Constraint<'db>>,
|
||||
@@ -490,10 +384,6 @@ impl<'map, 'db> Iterator for BindingWithConstraintsIterator<'map, 'db> {
|
||||
all_constraints: self.all_constraints,
|
||||
constraint_ids: def_id_with_constraints.constraint_ids,
|
||||
},
|
||||
constraints_active_at_binding: ConstraintsIterator {
|
||||
all_constraints: self.all_constraints,
|
||||
constraint_ids: def_id_with_constraints.constraints_active_at_binding_ids,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -503,16 +393,14 @@ impl std::iter::FusedIterator for BindingWithConstraintsIterator<'_, '_> {}
|
||||
pub(crate) struct BindingWithConstraints<'map, 'db> {
|
||||
pub(crate) binding: Definition<'db>,
|
||||
pub(crate) constraints: ConstraintsIterator<'map, 'db>,
|
||||
pub(crate) constraints_active_at_binding: ConstraintsIterator<'map, 'db>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ConstraintsIterator<'map, 'db> {
|
||||
all_constraints: &'map IndexVec<ScopedConstraintId, Constraint<'db>>,
|
||||
constraint_ids: ConstraintIdIterator<'map>,
|
||||
}
|
||||
|
||||
impl<'map, 'db> Iterator for ConstraintsIterator<'map, 'db> {
|
||||
impl<'db> Iterator for ConstraintsIterator<'_, 'db> {
|
||||
type Item = Constraint<'db>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@@ -526,7 +414,6 @@ impl std::iter::FusedIterator for ConstraintsIterator<'_, '_> {}
|
||||
|
||||
pub(crate) struct DeclarationsIterator<'map, 'db> {
|
||||
all_definitions: &'map IndexVec<ScopedDefinitionId, Definition<'db>>,
|
||||
all_constraints: &'map IndexVec<ScopedConstraintId, Constraint<'db>>,
|
||||
inner: DeclarationIdIterator<'map>,
|
||||
may_be_undeclared: bool,
|
||||
}
|
||||
@@ -537,19 +424,11 @@ impl DeclarationsIterator<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'map, 'db> Iterator for DeclarationsIterator<'map, 'db> {
|
||||
type Item = (Definition<'db>, ConstraintsIterator<'map, 'db>);
|
||||
impl<'db> Iterator for DeclarationsIterator<'_, 'db> {
|
||||
type Item = Definition<'db>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(|(def_id, constraints)| {
|
||||
(
|
||||
self.all_definitions[def_id],
|
||||
ConstraintsIterator {
|
||||
all_constraints: self.all_constraints,
|
||||
constraint_ids: constraints,
|
||||
},
|
||||
)
|
||||
})
|
||||
self.inner.next().map(|def_id| self.all_definitions[def_id])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,9 +440,6 @@ pub(super) struct FlowSnapshot {
|
||||
symbol_states: IndexVec<ScopedSymbolId, SymbolState>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct ActiveConstraintsSnapshot(HashSet<ScopedConstraintId>);
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct UseDefMapBuilder<'db> {
|
||||
/// Append-only array of [`Definition`].
|
||||
@@ -572,8 +448,6 @@ pub(super) struct UseDefMapBuilder<'db> {
|
||||
/// Append-only array of [`Constraint`].
|
||||
all_constraints: IndexVec<ScopedConstraintId, Constraint<'db>>,
|
||||
|
||||
active_constraints: HashSet<ScopedConstraintId>,
|
||||
|
||||
/// Live bindings at each so-far-recorded use.
|
||||
bindings_by_use: IndexVec<ScopedUseId, SymbolBindings>,
|
||||
|
||||
@@ -597,7 +471,7 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||
binding,
|
||||
SymbolDefinitions::Declarations(symbol_state.declarations().clone()),
|
||||
);
|
||||
symbol_state.record_binding(def_id, &self.active_constraints);
|
||||
symbol_state.record_binding(def_id);
|
||||
}
|
||||
|
||||
pub(super) fn record_constraint(&mut self, constraint: Constraint<'db>) {
|
||||
@@ -605,7 +479,6 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||
for state in &mut self.symbol_states {
|
||||
state.record_constraint(constraint_id);
|
||||
}
|
||||
self.active_constraints.insert(constraint_id);
|
||||
}
|
||||
|
||||
pub(super) fn record_declaration(
|
||||
@@ -619,7 +492,7 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||
declaration,
|
||||
SymbolDefinitions::Bindings(symbol_state.bindings().clone()),
|
||||
);
|
||||
symbol_state.record_declaration(def_id, &self.active_constraints);
|
||||
symbol_state.record_declaration(def_id);
|
||||
}
|
||||
|
||||
pub(super) fn record_declaration_and_binding(
|
||||
@@ -630,8 +503,8 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||
// We don't need to store anything in self.definitions_by_definition.
|
||||
let def_id = self.all_definitions.push(definition);
|
||||
let symbol_state = &mut self.symbol_states[symbol];
|
||||
symbol_state.record_declaration(def_id, &self.active_constraints);
|
||||
symbol_state.record_binding(def_id, &self.active_constraints);
|
||||
symbol_state.record_declaration(def_id);
|
||||
symbol_state.record_binding(def_id);
|
||||
}
|
||||
|
||||
pub(super) fn record_use(&mut self, symbol: ScopedSymbolId, use_id: ScopedUseId) {
|
||||
@@ -650,10 +523,6 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn constraints_snapshot(&self) -> ActiveConstraintsSnapshot {
|
||||
ActiveConstraintsSnapshot(self.active_constraints.clone())
|
||||
}
|
||||
|
||||
/// Restore the current builder symbols state to the given snapshot.
|
||||
pub(super) fn restore(&mut self, snapshot: FlowSnapshot) {
|
||||
// We never remove symbols from `symbol_states` (it's an IndexVec, and the symbol
|
||||
@@ -672,10 +541,6 @@ impl<'db> UseDefMapBuilder<'db> {
|
||||
.resize(num_symbols, SymbolState::undefined());
|
||||
}
|
||||
|
||||
pub(super) fn restore_constraints(&mut self, snapshot: ActiveConstraintsSnapshot) {
|
||||
self.active_constraints = snapshot.0;
|
||||
}
|
||||
|
||||
/// Merge the given snapshot into the current state, reflecting that we might have taken either
|
||||
/// path to get here. The new state for each symbol should include definitions from both the
|
||||
/// prior state and the snapshot.
|
||||
|
||||
@@ -122,7 +122,7 @@ impl<const B: usize> BitSet<B> {
|
||||
}
|
||||
|
||||
/// Iterator over values in a [`BitSet`].
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct BitSetIterator<'a, const B: usize> {
|
||||
/// The blocks we are iterating over.
|
||||
blocks: &'a [u64],
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
//!
|
||||
//! Tracking live declarations is simpler, since constraints are not involved, but otherwise very
|
||||
//! similar to tracking live bindings.
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::bitset::{BitSet, BitSetIterator};
|
||||
use ruff_index::newtype_index;
|
||||
use smallvec::SmallVec;
|
||||
@@ -89,8 +87,6 @@ pub(super) struct SymbolDeclarations {
|
||||
/// [`BitSet`]: which declarations (as [`ScopedDefinitionId`]) can reach the current location?
|
||||
live_declarations: Declarations,
|
||||
|
||||
constraints_active_at_declaration: Constraints, // TODO: rename to constraints_active_at_declaration
|
||||
|
||||
/// Could the symbol be un-declared at this point?
|
||||
may_be_undeclared: bool,
|
||||
}
|
||||
@@ -99,27 +95,14 @@ impl SymbolDeclarations {
|
||||
fn undeclared() -> Self {
|
||||
Self {
|
||||
live_declarations: Declarations::default(),
|
||||
constraints_active_at_declaration: Constraints::default(),
|
||||
may_be_undeclared: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Record a newly-encountered declaration for this symbol.
|
||||
fn record_declaration(
|
||||
&mut self,
|
||||
declaration_id: ScopedDefinitionId,
|
||||
active_constraints: &HashSet<ScopedConstraintId>,
|
||||
) {
|
||||
fn record_declaration(&mut self, declaration_id: ScopedDefinitionId) {
|
||||
self.live_declarations = Declarations::with(declaration_id.into());
|
||||
self.may_be_undeclared = false;
|
||||
|
||||
// TODO: unify code with below
|
||||
self.constraints_active_at_declaration = Constraints::with_capacity(1);
|
||||
self.constraints_active_at_declaration
|
||||
.push(BitSet::default());
|
||||
for active_constraint_id in active_constraints {
|
||||
self.constraints_active_at_declaration[0].insert(active_constraint_id.as_u32());
|
||||
}
|
||||
}
|
||||
|
||||
/// Add undeclared as a possibility for this symbol.
|
||||
@@ -131,7 +114,6 @@ impl SymbolDeclarations {
|
||||
pub(super) fn iter(&self) -> DeclarationIdIterator {
|
||||
DeclarationIdIterator {
|
||||
inner: self.live_declarations.iter(),
|
||||
constraints_active_at_binding: self.constraints_active_at_declaration.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,8 +138,6 @@ pub(super) struct SymbolBindings {
|
||||
/// binding in `live_bindings`.
|
||||
constraints: Constraints,
|
||||
|
||||
constraints_active_at_binding: Constraints,
|
||||
|
||||
/// Could the symbol be unbound at this point?
|
||||
may_be_unbound: bool,
|
||||
}
|
||||
@@ -167,7 +147,6 @@ impl SymbolBindings {
|
||||
Self {
|
||||
live_bindings: Bindings::default(),
|
||||
constraints: Constraints::default(),
|
||||
constraints_active_at_binding: Constraints::default(),
|
||||
may_be_unbound: true,
|
||||
}
|
||||
}
|
||||
@@ -178,21 +157,12 @@ impl SymbolBindings {
|
||||
}
|
||||
|
||||
/// Record a newly-encountered binding for this symbol.
|
||||
pub(super) fn record_binding(
|
||||
&mut self,
|
||||
binding_id: ScopedDefinitionId,
|
||||
active_constraints: &HashSet<ScopedConstraintId>,
|
||||
) {
|
||||
pub(super) fn record_binding(&mut self, binding_id: ScopedDefinitionId) {
|
||||
// The new binding replaces all previous live bindings in this path, and has no
|
||||
// constraints.
|
||||
self.live_bindings = Bindings::with(binding_id.into());
|
||||
self.constraints = Constraints::with_capacity(1);
|
||||
self.constraints.push(BitSet::default());
|
||||
self.constraints_active_at_binding = Constraints::with_capacity(1);
|
||||
self.constraints_active_at_binding.push(BitSet::default());
|
||||
for active_constraint_id in active_constraints {
|
||||
self.constraints_active_at_binding[0].insert(active_constraint_id.as_u32());
|
||||
}
|
||||
self.may_be_unbound = false;
|
||||
}
|
||||
|
||||
@@ -208,7 +178,6 @@ impl SymbolBindings {
|
||||
BindingIdWithConstraintsIterator {
|
||||
definitions: self.live_bindings.iter(),
|
||||
constraints: self.constraints.iter(),
|
||||
constraints_active_at_binding: self.constraints_active_at_binding.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,12 +207,8 @@ impl SymbolState {
|
||||
}
|
||||
|
||||
/// Record a newly-encountered binding for this symbol.
|
||||
pub(super) fn record_binding(
|
||||
&mut self,
|
||||
binding_id: ScopedDefinitionId,
|
||||
active_constraints: &HashSet<ScopedConstraintId>,
|
||||
) {
|
||||
self.bindings.record_binding(binding_id, active_constraints);
|
||||
pub(super) fn record_binding(&mut self, binding_id: ScopedDefinitionId) {
|
||||
self.bindings.record_binding(binding_id);
|
||||
}
|
||||
|
||||
/// Add given constraint to all live bindings.
|
||||
@@ -257,13 +222,8 @@ impl SymbolState {
|
||||
}
|
||||
|
||||
/// Record a newly-encountered declaration of this symbol.
|
||||
pub(super) fn record_declaration(
|
||||
&mut self,
|
||||
declaration_id: ScopedDefinitionId,
|
||||
active_constraints: &HashSet<ScopedConstraintId>,
|
||||
) {
|
||||
self.declarations
|
||||
.record_declaration(declaration_id, active_constraints);
|
||||
pub(super) fn record_declaration(&mut self, declaration_id: ScopedDefinitionId) {
|
||||
self.declarations.record_declaration(declaration_id);
|
||||
}
|
||||
|
||||
/// Merge another [`SymbolState`] into this one.
|
||||
@@ -272,93 +232,24 @@ impl SymbolState {
|
||||
bindings: SymbolBindings {
|
||||
live_bindings: Bindings::default(),
|
||||
constraints: Constraints::default(),
|
||||
constraints_active_at_binding: Constraints::default(), // TODO
|
||||
may_be_unbound: self.bindings.may_be_unbound || b.bindings.may_be_unbound,
|
||||
},
|
||||
declarations: SymbolDeclarations {
|
||||
live_declarations: self.declarations.live_declarations.clone(),
|
||||
constraints_active_at_declaration: Constraints::default(), // TODO
|
||||
may_be_undeclared: self.declarations.may_be_undeclared
|
||||
|| b.declarations.may_be_undeclared,
|
||||
},
|
||||
};
|
||||
|
||||
// let mut constraints_active_at_binding = BitSet::default();
|
||||
// for active_constraint_id in active_constraints.0 {
|
||||
// constraints_active_at_binding.insert(active_constraint_id.as_u32());
|
||||
// }
|
||||
|
||||
std::mem::swap(&mut a, self);
|
||||
// self.declarations
|
||||
// .live_declarations
|
||||
// .union(&b.declarations.live_declarations);
|
||||
|
||||
let mut a_decls_iter = a.declarations.live_declarations.iter();
|
||||
let mut b_decls_iter = b.declarations.live_declarations.iter();
|
||||
let mut a_constraints_active_at_declaration_iter =
|
||||
a.declarations.constraints_active_at_declaration.into_iter();
|
||||
let mut b_constraints_active_at_declaration_iter =
|
||||
b.declarations.constraints_active_at_declaration.into_iter();
|
||||
|
||||
let mut opt_a_decl: Option<u32> = a_decls_iter.next();
|
||||
let mut opt_b_decl: Option<u32> = b_decls_iter.next();
|
||||
|
||||
let push = |decl,
|
||||
constraints_active_at_declaration_iter: &mut ConstraintsIntoIterator,
|
||||
merged: &mut Self| {
|
||||
merged.declarations.live_declarations.insert(decl);
|
||||
let constraints_active_at_binding = constraints_active_at_declaration_iter
|
||||
.next()
|
||||
.expect("declarations and constraints_active_at_binding length mismatch");
|
||||
merged
|
||||
.declarations
|
||||
.constraints_active_at_declaration
|
||||
.push(constraints_active_at_binding);
|
||||
};
|
||||
|
||||
loop {
|
||||
match (opt_a_decl, opt_b_decl) {
|
||||
(Some(a_decl), Some(b_decl)) => match a_decl.cmp(&b_decl) {
|
||||
std::cmp::Ordering::Less => {
|
||||
push(a_decl, &mut a_constraints_active_at_declaration_iter, self);
|
||||
opt_a_decl = a_decls_iter.next();
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
push(b_decl, &mut b_constraints_active_at_declaration_iter, self);
|
||||
opt_b_decl = b_decls_iter.next();
|
||||
}
|
||||
std::cmp::Ordering::Equal => {
|
||||
push(a_decl, &mut b_constraints_active_at_declaration_iter, self);
|
||||
self.declarations
|
||||
.constraints_active_at_declaration
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.intersect(&a_constraints_active_at_declaration_iter.next().unwrap());
|
||||
|
||||
opt_a_decl = a_decls_iter.next();
|
||||
opt_b_decl = b_decls_iter.next();
|
||||
}
|
||||
},
|
||||
(Some(a_decl), None) => {
|
||||
push(a_decl, &mut a_constraints_active_at_declaration_iter, self);
|
||||
opt_a_decl = a_decls_iter.next();
|
||||
}
|
||||
(None, Some(b_decl)) => {
|
||||
push(b_decl, &mut b_constraints_active_at_declaration_iter, self);
|
||||
opt_b_decl = b_decls_iter.next();
|
||||
}
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
self.declarations
|
||||
.live_declarations
|
||||
.union(&b.declarations.live_declarations);
|
||||
|
||||
let mut a_defs_iter = a.bindings.live_bindings.iter();
|
||||
let mut b_defs_iter = b.bindings.live_bindings.iter();
|
||||
let mut a_constraints_iter = a.bindings.constraints.into_iter();
|
||||
let mut b_constraints_iter = b.bindings.constraints.into_iter();
|
||||
let mut a_constraints_active_at_binding_iter =
|
||||
a.bindings.constraints_active_at_binding.into_iter();
|
||||
let mut b_constraints_active_at_binding_iter =
|
||||
b.bindings.constraints_active_at_binding.into_iter();
|
||||
|
||||
let mut opt_a_def: Option<u32> = a_defs_iter.next();
|
||||
let mut opt_b_def: Option<u32> = b_defs_iter.next();
|
||||
@@ -370,10 +261,7 @@ impl SymbolState {
|
||||
// path is irrelevant.
|
||||
|
||||
// Helper to push `def`, with constraints in `constraints_iter`, onto `self`.
|
||||
let push = |def,
|
||||
constraints_iter: &mut ConstraintsIntoIterator,
|
||||
constraints_active_at_binding_iter: &mut ConstraintsIntoIterator,
|
||||
merged: &mut Self| {
|
||||
let push = |def, constraints_iter: &mut ConstraintsIntoIterator, merged: &mut Self| {
|
||||
merged.bindings.live_bindings.insert(def);
|
||||
// SAFETY: we only ever create SymbolState with either no definitions and no constraint
|
||||
// bitsets (`::unbound`) or one definition and one constraint bitset (`::with`), and
|
||||
@@ -383,14 +271,7 @@ impl SymbolState {
|
||||
let constraints = constraints_iter
|
||||
.next()
|
||||
.expect("definitions and constraints length mismatch");
|
||||
let constraints_active_at_binding = constraints_active_at_binding_iter
|
||||
.next()
|
||||
.expect("definitions and constraints_active_at_binding length mismatch");
|
||||
merged.bindings.constraints.push(constraints);
|
||||
merged
|
||||
.bindings
|
||||
.constraints_active_at_binding
|
||||
.push(constraints_active_at_binding);
|
||||
};
|
||||
|
||||
loop {
|
||||
@@ -398,32 +279,17 @@ impl SymbolState {
|
||||
(Some(a_def), Some(b_def)) => match a_def.cmp(&b_def) {
|
||||
std::cmp::Ordering::Less => {
|
||||
// Next definition ID is only in `a`, push it to `self` and advance `a`.
|
||||
push(
|
||||
a_def,
|
||||
&mut a_constraints_iter,
|
||||
&mut a_constraints_active_at_binding_iter,
|
||||
self,
|
||||
);
|
||||
push(a_def, &mut a_constraints_iter, self);
|
||||
opt_a_def = a_defs_iter.next();
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
// Next definition ID is only in `b`, push it to `self` and advance `b`.
|
||||
push(
|
||||
b_def,
|
||||
&mut b_constraints_iter,
|
||||
&mut b_constraints_active_at_binding_iter,
|
||||
self,
|
||||
);
|
||||
push(b_def, &mut b_constraints_iter, self);
|
||||
opt_b_def = b_defs_iter.next();
|
||||
}
|
||||
std::cmp::Ordering::Equal => {
|
||||
// Next definition is in both; push to `self` and intersect constraints.
|
||||
push(
|
||||
a_def,
|
||||
&mut b_constraints_iter,
|
||||
&mut b_constraints_active_at_binding_iter,
|
||||
self,
|
||||
);
|
||||
push(a_def, &mut b_constraints_iter, self);
|
||||
// SAFETY: we only ever create SymbolState with either no definitions and
|
||||
// no constraint bitsets (`::unbound`) or one definition and one constraint
|
||||
// bitset (`::with`), and `::merge` always pushes one definition and one
|
||||
@@ -432,11 +298,6 @@ impl SymbolState {
|
||||
let a_constraints = a_constraints_iter
|
||||
.next()
|
||||
.expect("definitions and constraints length mismatch");
|
||||
// let _a_constraints_active_at_binding =
|
||||
// a_constraints_active_at_binding_iter.next().expect(
|
||||
// "definitions and constraints_active_at_binding length mismatch",
|
||||
// ); // TODO: perform check that we see the same constraints in both paths
|
||||
|
||||
// If the same definition is visible through both paths, any constraint
|
||||
// that applies on only one path is irrelevant to the resulting type from
|
||||
// unioning the two paths, so we intersect the constraints.
|
||||
@@ -445,29 +306,18 @@ impl SymbolState {
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.intersect(&a_constraints);
|
||||
|
||||
opt_a_def = a_defs_iter.next();
|
||||
opt_b_def = b_defs_iter.next();
|
||||
}
|
||||
},
|
||||
(Some(a_def), None) => {
|
||||
// We've exhausted `b`, just push the def from `a` and move on to the next.
|
||||
push(
|
||||
a_def,
|
||||
&mut a_constraints_iter,
|
||||
&mut a_constraints_active_at_binding_iter,
|
||||
self,
|
||||
);
|
||||
push(a_def, &mut a_constraints_iter, self);
|
||||
opt_a_def = a_defs_iter.next();
|
||||
}
|
||||
(None, Some(b_def)) => {
|
||||
// We've exhausted `a`, just push the def from `b` and move on to the next.
|
||||
push(
|
||||
b_def,
|
||||
&mut b_constraints_iter,
|
||||
&mut b_constraints_active_at_binding_iter,
|
||||
self,
|
||||
);
|
||||
push(b_def, &mut b_constraints_iter, self);
|
||||
opt_b_def = b_defs_iter.next();
|
||||
}
|
||||
(None, None) => break,
|
||||
@@ -503,37 +353,26 @@ impl Default for SymbolState {
|
||||
pub(super) struct BindingIdWithConstraints<'a> {
|
||||
pub(super) definition: ScopedDefinitionId,
|
||||
pub(super) constraint_ids: ConstraintIdIterator<'a>,
|
||||
pub(super) constraints_active_at_binding_ids: ConstraintIdIterator<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct BindingIdWithConstraintsIterator<'a> {
|
||||
definitions: BindingsIterator<'a>,
|
||||
constraints: ConstraintsIterator<'a>,
|
||||
constraints_active_at_binding: ConstraintsIterator<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BindingIdWithConstraintsIterator<'a> {
|
||||
type Item = BindingIdWithConstraints<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match (
|
||||
self.definitions.next(),
|
||||
self.constraints.next(),
|
||||
self.constraints_active_at_binding.next(),
|
||||
) {
|
||||
(None, None, None) => None,
|
||||
(Some(def), Some(constraints), Some(constraints_active_at_binding)) => {
|
||||
Some(BindingIdWithConstraints {
|
||||
definition: ScopedDefinitionId::from_u32(def),
|
||||
constraint_ids: ConstraintIdIterator {
|
||||
wrapped: constraints.iter(),
|
||||
},
|
||||
constraints_active_at_binding_ids: ConstraintIdIterator {
|
||||
wrapped: constraints_active_at_binding.iter(),
|
||||
},
|
||||
})
|
||||
}
|
||||
match (self.definitions.next(), self.constraints.next()) {
|
||||
(None, None) => None,
|
||||
(Some(def), Some(constraints)) => Some(BindingIdWithConstraints {
|
||||
definition: ScopedDefinitionId::from_u32(def),
|
||||
constraint_ids: ConstraintIdIterator {
|
||||
wrapped: constraints.iter(),
|
||||
},
|
||||
}),
|
||||
// SAFETY: see above.
|
||||
_ => unreachable!("definitions and constraints length mismatch"),
|
||||
}
|
||||
@@ -542,7 +381,7 @@ impl<'a> Iterator for BindingIdWithConstraintsIterator<'a> {
|
||||
|
||||
impl std::iter::FusedIterator for BindingIdWithConstraintsIterator<'_> {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ConstraintIdIterator<'a> {
|
||||
wrapped: BitSetIterator<'a, INLINE_CONSTRAINT_BLOCKS>,
|
||||
}
|
||||
@@ -560,25 +399,13 @@ impl std::iter::FusedIterator for ConstraintIdIterator<'_> {}
|
||||
#[derive(Debug)]
|
||||
pub(super) struct DeclarationIdIterator<'a> {
|
||||
inner: DeclarationsIterator<'a>,
|
||||
constraints_active_at_binding: ConstraintsIterator<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DeclarationIdIterator<'a> {
|
||||
type Item = (ScopedDefinitionId, ConstraintIdIterator<'a>);
|
||||
impl Iterator for DeclarationIdIterator<'_> {
|
||||
type Item = ScopedDefinitionId;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// self.inner.next().map(ScopedDefinitionId::from_u32)
|
||||
match (self.inner.next(), self.constraints_active_at_binding.next()) {
|
||||
(None, None) => None,
|
||||
(Some(declaration), Some(constraints_active_at_binding)) => Some((
|
||||
ScopedDefinitionId::from_u32(declaration),
|
||||
ConstraintIdIterator {
|
||||
wrapped: constraints_active_at_binding.iter(),
|
||||
},
|
||||
)),
|
||||
// SAFETY: see above.
|
||||
_ => unreachable!("declarations and constraints_active_at_binding length mismatch"),
|
||||
}
|
||||
self.inner.next().map(ScopedDefinitionId::from_u32)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,7 +413,7 @@ impl std::iter::FusedIterator for DeclarationIdIterator<'_> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ScopedConstraintId, SymbolState};
|
||||
use super::{ScopedConstraintId, ScopedDefinitionId, SymbolState};
|
||||
|
||||
fn assert_bindings(symbol: &SymbolState, may_be_unbound: bool, expected: &[&str]) {
|
||||
assert_eq!(symbol.may_be_unbound(), may_be_unbound);
|
||||
@@ -618,7 +445,7 @@ mod tests {
|
||||
let actual = symbol
|
||||
.declarations()
|
||||
.iter()
|
||||
.map(|(d, _)| d.as_u32()) // TODO: constraints
|
||||
.map(ScopedDefinitionId::as_u32)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@@ -630,76 +457,76 @@ mod tests {
|
||||
assert_bindings(&sym, true, &[]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn with() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
#[test]
|
||||
fn with() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
|
||||
// assert_bindings(&sym, false, &["0<>"]);
|
||||
// }
|
||||
assert_bindings(&sym, false, &["0<>"]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn set_may_be_unbound() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
// sym.set_may_be_unbound();
|
||||
#[test]
|
||||
fn set_may_be_unbound() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
sym.set_may_be_unbound();
|
||||
|
||||
// assert_bindings(&sym, true, &["0<>"]);
|
||||
// }
|
||||
assert_bindings(&sym, true, &["0<>"]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn record_constraint() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
// sym.record_constraint(ScopedConstraintId::from_u32(0));
|
||||
#[test]
|
||||
fn record_constraint() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
sym.record_constraint(ScopedConstraintId::from_u32(0));
|
||||
|
||||
// assert_bindings(&sym, false, &["0<0>"]);
|
||||
// }
|
||||
assert_bindings(&sym, false, &["0<0>"]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn merge() {
|
||||
// // merging the same definition with the same constraint keeps the constraint
|
||||
// let mut sym0a = SymbolState::undefined();
|
||||
// sym0a.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
// sym0a.record_constraint(ScopedConstraintId::from_u32(0));
|
||||
#[test]
|
||||
fn merge() {
|
||||
// merging the same definition with the same constraint keeps the constraint
|
||||
let mut sym0a = SymbolState::undefined();
|
||||
sym0a.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
sym0a.record_constraint(ScopedConstraintId::from_u32(0));
|
||||
|
||||
// let mut sym0b = SymbolState::undefined();
|
||||
// sym0b.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
// sym0b.record_constraint(ScopedConstraintId::from_u32(0));
|
||||
let mut sym0b = SymbolState::undefined();
|
||||
sym0b.record_binding(ScopedDefinitionId::from_u32(0));
|
||||
sym0b.record_constraint(ScopedConstraintId::from_u32(0));
|
||||
|
||||
// sym0a.merge(sym0b);
|
||||
// let mut sym0 = sym0a;
|
||||
// assert_bindings(&sym0, false, &["0<0>"]);
|
||||
sym0a.merge(sym0b);
|
||||
let mut sym0 = sym0a;
|
||||
assert_bindings(&sym0, false, &["0<0>"]);
|
||||
|
||||
// // merging the same definition with differing constraints drops all constraints
|
||||
// let mut sym1a = SymbolState::undefined();
|
||||
// sym1a.record_binding(ScopedDefinitionId::from_u32(1));
|
||||
// sym1a.record_constraint(ScopedConstraintId::from_u32(1));
|
||||
// merging the same definition with differing constraints drops all constraints
|
||||
let mut sym1a = SymbolState::undefined();
|
||||
sym1a.record_binding(ScopedDefinitionId::from_u32(1));
|
||||
sym1a.record_constraint(ScopedConstraintId::from_u32(1));
|
||||
|
||||
// let mut sym1b = SymbolState::undefined();
|
||||
// sym1b.record_binding(ScopedDefinitionId::from_u32(1));
|
||||
// sym1b.record_constraint(ScopedConstraintId::from_u32(2));
|
||||
let mut sym1b = SymbolState::undefined();
|
||||
sym1b.record_binding(ScopedDefinitionId::from_u32(1));
|
||||
sym1b.record_constraint(ScopedConstraintId::from_u32(2));
|
||||
|
||||
// sym1a.merge(sym1b);
|
||||
// let sym1 = sym1a;
|
||||
// assert_bindings(&sym1, false, &["1<>"]);
|
||||
sym1a.merge(sym1b);
|
||||
let sym1 = sym1a;
|
||||
assert_bindings(&sym1, false, &["1<>"]);
|
||||
|
||||
// // merging a constrained definition with unbound keeps both
|
||||
// let mut sym2a = SymbolState::undefined();
|
||||
// sym2a.record_binding(ScopedDefinitionId::from_u32(2));
|
||||
// sym2a.record_constraint(ScopedConstraintId::from_u32(3));
|
||||
// merging a constrained definition with unbound keeps both
|
||||
let mut sym2a = SymbolState::undefined();
|
||||
sym2a.record_binding(ScopedDefinitionId::from_u32(2));
|
||||
sym2a.record_constraint(ScopedConstraintId::from_u32(3));
|
||||
|
||||
// let sym2b = SymbolState::undefined();
|
||||
let sym2b = SymbolState::undefined();
|
||||
|
||||
// sym2a.merge(sym2b);
|
||||
// let sym2 = sym2a;
|
||||
// assert_bindings(&sym2, true, &["2<3>"]);
|
||||
sym2a.merge(sym2b);
|
||||
let sym2 = sym2a;
|
||||
assert_bindings(&sym2, true, &["2<3>"]);
|
||||
|
||||
// // merging different definitions keeps them each with their existing constraints
|
||||
// sym0.merge(sym2);
|
||||
// let sym = sym0;
|
||||
// assert_bindings(&sym, true, &["0<0>", "2<3>"]);
|
||||
// }
|
||||
// merging different definitions keeps them each with their existing constraints
|
||||
sym0.merge(sym2);
|
||||
let sym = sym0;
|
||||
assert_bindings(&sym, true, &["0<0>", "2<3>"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_declaration() {
|
||||
@@ -708,54 +535,54 @@ mod tests {
|
||||
assert_declarations(&sym, true, &[]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn record_declaration() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
#[test]
|
||||
fn record_declaration() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
|
||||
// assert_declarations(&sym, false, &[1]);
|
||||
// }
|
||||
assert_declarations(&sym, false, &[1]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn record_declaration_override() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
// sym.record_declaration(ScopedDefinitionId::from_u32(2));
|
||||
#[test]
|
||||
fn record_declaration_override() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
sym.record_declaration(ScopedDefinitionId::from_u32(2));
|
||||
|
||||
// assert_declarations(&sym, false, &[2]);
|
||||
// }
|
||||
assert_declarations(&sym, false, &[2]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn record_declaration_merge() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
#[test]
|
||||
fn record_declaration_merge() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
|
||||
// let mut sym2 = SymbolState::undefined();
|
||||
// sym2.record_declaration(ScopedDefinitionId::from_u32(2));
|
||||
let mut sym2 = SymbolState::undefined();
|
||||
sym2.record_declaration(ScopedDefinitionId::from_u32(2));
|
||||
|
||||
// sym.merge(sym2);
|
||||
sym.merge(sym2);
|
||||
|
||||
// assert_declarations(&sym, false, &[1, 2]);
|
||||
// }
|
||||
assert_declarations(&sym, false, &[1, 2]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn record_declaration_merge_partial_undeclared() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
#[test]
|
||||
fn record_declaration_merge_partial_undeclared() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_declaration(ScopedDefinitionId::from_u32(1));
|
||||
|
||||
// let sym2 = SymbolState::undefined();
|
||||
let sym2 = SymbolState::undefined();
|
||||
|
||||
// sym.merge(sym2);
|
||||
sym.merge(sym2);
|
||||
|
||||
// assert_declarations(&sym, true, &[1]);
|
||||
// }
|
||||
assert_declarations(&sym, true, &[1]);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn set_may_be_undeclared() {
|
||||
// let mut sym = SymbolState::undefined();
|
||||
// sym.record_declaration(ScopedDefinitionId::from_u32(0));
|
||||
// sym.set_may_be_undeclared();
|
||||
#[test]
|
||||
fn set_may_be_undeclared() {
|
||||
let mut sym = SymbolState::undefined();
|
||||
sym.record_declaration(ScopedDefinitionId::from_u32(0));
|
||||
sym.set_may_be_undeclared();
|
||||
|
||||
// assert_declarations(&sym, true, &[0]);
|
||||
// }
|
||||
assert_declarations(&sym, true, &[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,31 +166,15 @@ impl_binding_has_ty!(ast::ParameterWithDefault);
|
||||
mod tests {
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::{HasTy, ProgramSettings, SemanticModel};
|
||||
|
||||
fn setup_db<'a>(files: impl IntoIterator<Item = (&'a str, &'a str)>) -> anyhow::Result<TestDb> {
|
||||
let mut db = TestDb::new();
|
||||
db.write_files(files)?;
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: PythonVersion::default(),
|
||||
search_paths: SearchPathSettings::new(SystemPathBuf::from("/src")),
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
use crate::db::tests::TestDbBuilder;
|
||||
use crate::{HasTy, SemanticModel};
|
||||
|
||||
#[test]
|
||||
fn function_ty() -> anyhow::Result<()> {
|
||||
let db = setup_db([("/src/foo.py", "def test(): pass")])?;
|
||||
let db = TestDbBuilder::new()
|
||||
.with_file("/src/foo.py", "def test(): pass")
|
||||
.build()?;
|
||||
|
||||
let foo = system_path_to_file(&db, "/src/foo.py").unwrap();
|
||||
|
||||
@@ -207,7 +191,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn class_ty() -> anyhow::Result<()> {
|
||||
let db = setup_db([("/src/foo.py", "class Test: pass")])?;
|
||||
let db = TestDbBuilder::new()
|
||||
.with_file("/src/foo.py", "class Test: pass")
|
||||
.build()?;
|
||||
|
||||
let foo = system_path_to_file(&db, "/src/foo.py").unwrap();
|
||||
|
||||
@@ -224,10 +210,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn alias_ty() -> anyhow::Result<()> {
|
||||
let db = setup_db([
|
||||
("/src/foo.py", "class Test: pass"),
|
||||
("/src/bar.py", "from foo import Test"),
|
||||
])?;
|
||||
let db = TestDbBuilder::new()
|
||||
.with_file("/src/foo.py", "class Test: pass")
|
||||
.with_file("/src/bar.py", "from foo import Test")
|
||||
.build()?;
|
||||
|
||||
let bar = system_path_to_file(&db, "/src/bar.py").unwrap();
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ impl<'db> Symbol<'db> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::tests::setup_db;
|
||||
use crate::db::tests::setup_db;
|
||||
|
||||
#[test]
|
||||
fn test_symbol_or_fall_back_to() {
|
||||
|
||||
@@ -15,7 +15,6 @@ pub(crate) use self::infer::{
|
||||
pub(crate) use self::signatures::Signature;
|
||||
use crate::module_resolver::file_to_module;
|
||||
use crate::semantic_index::ast_ids::HasScopedExpressionId;
|
||||
use crate::semantic_index::constraint::ConstraintNode;
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::symbol::{self as symbol, ScopeId, ScopedSymbolId};
|
||||
use crate::semantic_index::{
|
||||
@@ -29,7 +28,7 @@ use crate::symbol::{Boundness, Symbol};
|
||||
use crate::types::diagnostic::TypeCheckDiagnosticsBuilder;
|
||||
use crate::types::mro::{ClassBase, Mro, MroError, MroIterator};
|
||||
use crate::types::narrow::narrowing_constraint;
|
||||
use crate::{Db, FxOrderSet, Module, Program};
|
||||
use crate::{Db, FxOrderSet, Module, Program, PythonVersion};
|
||||
|
||||
mod builder;
|
||||
mod diagnostic;
|
||||
@@ -41,6 +40,9 @@ mod signatures;
|
||||
mod string_annotation;
|
||||
mod unpacker;
|
||||
|
||||
#[cfg(test)]
|
||||
mod property_tests;
|
||||
|
||||
#[salsa::tracked(return_ref)]
|
||||
pub fn check_types(db: &dyn Db, file: File) -> TypeCheckDiagnostics {
|
||||
let _span = tracing::trace_span!("check_types", file=?file.path(db)).entered();
|
||||
@@ -223,12 +225,6 @@ fn definition_expression_ty<'db>(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum UnconditionallyVisible {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
/// Infer the combined type of an iterator of bindings.
|
||||
///
|
||||
/// Will return a union if there is more than one binding.
|
||||
@@ -236,88 +232,29 @@ fn bindings_ty<'db>(
|
||||
db: &'db dyn Db,
|
||||
bindings_with_constraints: BindingWithConstraintsIterator<'_, 'db>,
|
||||
) -> Option<Type<'db>> {
|
||||
let def_types = bindings_with_constraints.map(
|
||||
let mut def_types = bindings_with_constraints.map(
|
||||
|BindingWithConstraints {
|
||||
binding,
|
||||
constraints,
|
||||
constraints_active_at_binding,
|
||||
}| {
|
||||
let test_expr_tys = || {
|
||||
constraints_active_at_binding.clone().map(|c| {
|
||||
let ty = if let ConstraintNode::Expression(test_expr) = c.node {
|
||||
let inference = infer_expression_types(db, test_expr);
|
||||
let scope = test_expr.scope(db);
|
||||
inference
|
||||
.expression_ty(test_expr.node_ref(db).scoped_expression_id(db, scope))
|
||||
} else {
|
||||
// TODO: handle other constraint nodes
|
||||
todo_type!()
|
||||
};
|
||||
let mut constraint_tys = constraints
|
||||
.filter_map(|constraint| narrowing_constraint(db, constraint, binding))
|
||||
.peekable();
|
||||
|
||||
(c, ty)
|
||||
})
|
||||
};
|
||||
|
||||
if test_expr_tys().any(|(c, test_expr_ty)| {
|
||||
if c.is_positive {
|
||||
test_expr_ty.bool(db).is_always_false()
|
||||
} else {
|
||||
test_expr_ty.bool(db).is_always_true()
|
||||
}
|
||||
}) {
|
||||
// TODO: do we need to call binding_ty(…) even if we don't need the result?
|
||||
(Type::Never, UnconditionallyVisible::No)
|
||||
let binding_ty = binding_ty(db, binding);
|
||||
if constraint_tys.peek().is_some() {
|
||||
constraint_tys
|
||||
.fold(
|
||||
IntersectionBuilder::new(db).add_positive(binding_ty),
|
||||
IntersectionBuilder::add_positive,
|
||||
)
|
||||
.build()
|
||||
} else {
|
||||
let mut test_expr_tys_iter = test_expr_tys().peekable();
|
||||
|
||||
let unconditionally_visible = if test_expr_tys_iter.peek().is_some()
|
||||
&& test_expr_tys_iter.all(|(c, test_expr_ty)| {
|
||||
if c.is_positive {
|
||||
test_expr_ty.bool(db).is_always_true()
|
||||
} else {
|
||||
test_expr_ty.bool(db).is_always_false()
|
||||
}
|
||||
}) {
|
||||
UnconditionallyVisible::Yes
|
||||
} else {
|
||||
UnconditionallyVisible::No
|
||||
};
|
||||
|
||||
let mut constraint_tys = constraints
|
||||
.filter_map(|constraint| narrowing_constraint(db, constraint, binding))
|
||||
.peekable();
|
||||
|
||||
let binding_ty = binding_ty(db, binding);
|
||||
if constraint_tys.peek().is_some() {
|
||||
let intersection_ty = constraint_tys
|
||||
.fold(
|
||||
IntersectionBuilder::new(db).add_positive(binding_ty),
|
||||
IntersectionBuilder::add_positive,
|
||||
)
|
||||
.build();
|
||||
(intersection_ty, unconditionally_visible)
|
||||
} else {
|
||||
(binding_ty, unconditionally_visible)
|
||||
}
|
||||
binding_ty
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: get rid of all the collects and clean up, obviously
|
||||
let def_types: Vec<_> = def_types.collect();
|
||||
|
||||
// shrink the vector to only include everything from the last unconditionally visible binding
|
||||
let def_types: Vec<_> = def_types
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while_inclusive(|(_, unconditionally_visible)| {
|
||||
*unconditionally_visible != UnconditionallyVisible::Yes
|
||||
})
|
||||
.map(|(ty, _)| *ty)
|
||||
.collect();
|
||||
|
||||
let mut def_types = def_types.into_iter().rev();
|
||||
|
||||
if let Some(first) = def_types.next() {
|
||||
if let Some(second) = def_types.next() {
|
||||
Some(UnionType::from_elements(
|
||||
@@ -353,63 +290,7 @@ fn declarations_ty<'db>(
|
||||
declarations: DeclarationsIterator<'_, 'db>,
|
||||
undeclared_ty: Option<Type<'db>>,
|
||||
) -> DeclaredTypeResult<'db> {
|
||||
let decl_types = declarations.map(|(declaration, constraints_active_at_declaration)| {
|
||||
let test_expr_tys = || {
|
||||
constraints_active_at_declaration.clone().map(|c| {
|
||||
let ty = if let ConstraintNode::Expression(test_expr) = c.node {
|
||||
let inference = infer_expression_types(db, test_expr);
|
||||
let scope = test_expr.scope(db);
|
||||
inference.expression_ty(test_expr.node_ref(db).scoped_expression_id(db, scope))
|
||||
} else {
|
||||
// TODO: handle other constraint nodes
|
||||
todo_type!()
|
||||
};
|
||||
|
||||
(c, ty)
|
||||
})
|
||||
};
|
||||
|
||||
if test_expr_tys().any(|(c, test_expr_ty)| {
|
||||
if c.is_positive {
|
||||
test_expr_ty.bool(db).is_always_false()
|
||||
} else {
|
||||
test_expr_ty.bool(db).is_always_true()
|
||||
}
|
||||
}) {
|
||||
(Type::Never, UnconditionallyVisible::No)
|
||||
} else {
|
||||
let mut test_expr_tys_iter = test_expr_tys().peekable();
|
||||
|
||||
if test_expr_tys_iter.peek().is_some()
|
||||
&& test_expr_tys_iter.all(|(c, test_expr_ty)| {
|
||||
if c.is_positive {
|
||||
test_expr_ty.bool(db).is_always_true()
|
||||
} else {
|
||||
test_expr_ty.bool(db).is_always_false()
|
||||
}
|
||||
})
|
||||
{
|
||||
(declaration_ty(db, declaration), UnconditionallyVisible::Yes)
|
||||
} else {
|
||||
(declaration_ty(db, declaration), UnconditionallyVisible::No)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: get rid of all the collects and clean up, obviously
|
||||
let decl_types: Vec<_> = decl_types.collect();
|
||||
|
||||
// shrink the vector to only include everything from the last unconditionally visible binding
|
||||
let decl_types: Vec<_> = decl_types
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while_inclusive(|(_, unconditionally_visible)| {
|
||||
*unconditionally_visible != UnconditionallyVisible::Yes
|
||||
})
|
||||
.map(|(ty, _)| *ty)
|
||||
.collect();
|
||||
|
||||
let decl_types = decl_types.into_iter().rev();
|
||||
let decl_types = declarations.map(|declaration| declaration_ty(db, declaration));
|
||||
|
||||
let mut all_types = undeclared_ty.into_iter().chain(decl_types);
|
||||
|
||||
@@ -421,13 +302,7 @@ fn declarations_ty<'db>(
|
||||
let declared_ty = if let Some(second) = all_types.next() {
|
||||
let mut builder = UnionBuilder::new(db).add(first);
|
||||
for other in [second].into_iter().chain(all_types) {
|
||||
// Make sure not to emit spurious errors relating to `Type::Todo`,
|
||||
// since we only infer this type due to a limitation in our current model.
|
||||
//
|
||||
// `Unknown` is different here, since we might infer `Unknown`
|
||||
// for one of these due to a variable being defined in one possible
|
||||
// control-flow branch but not another one.
|
||||
if !first.is_equivalent_to(db, other) && !first.is_todo() && !other.is_todo() {
|
||||
if !first.is_equivalent_to(db, other) {
|
||||
conflicting.push(other);
|
||||
}
|
||||
builder = builder.add(other);
|
||||
@@ -696,8 +571,11 @@ impl<'db> Type<'db> {
|
||||
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
|
||||
}
|
||||
|
||||
pub fn tuple(db: &'db dyn Db, elements: &[Type<'db>]) -> Self {
|
||||
Self::Tuple(TupleType::new(db, elements))
|
||||
pub fn tuple<T: Into<Type<'db>>>(
|
||||
db: &'db dyn Db,
|
||||
elements: impl IntoIterator<Item = T>,
|
||||
) -> Self {
|
||||
TupleType::from_elements(db, elements)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -716,11 +594,16 @@ impl<'db> Type<'db> {
|
||||
|
||||
/// Return true if this type is a [subtype of] type `target`.
|
||||
///
|
||||
/// This method returns `false` if either `self` or `other` is not fully static.
|
||||
///
|
||||
/// [subtype of]: https://typing.readthedocs.io/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
|
||||
pub(crate) fn is_subtype_of(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
||||
if self.is_equivalent_to(db, target) {
|
||||
return true;
|
||||
}
|
||||
if !self.is_fully_static(db) || !target.is_fully_static(db) {
|
||||
return false;
|
||||
}
|
||||
match (self, target) {
|
||||
(Type::Unknown | Type::Any | Type::Todo(_), _) => false,
|
||||
(_, Type::Unknown | Type::Any | Type::Todo(_)) => false,
|
||||
@@ -841,9 +724,6 @@ impl<'db> Type<'db> {
|
||||
(Type::KnownInstance(left), right) => {
|
||||
left.instance_fallback(db).is_subtype_of(db, right)
|
||||
}
|
||||
(left, Type::KnownInstance(right)) => {
|
||||
left.is_subtype_of(db, right.instance_fallback(db))
|
||||
}
|
||||
(Type::Instance(left), Type::Instance(right)) => left.is_instance_of(db, right.class),
|
||||
// TODO
|
||||
_ => false,
|
||||
@@ -883,14 +763,48 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if this type is equivalent to type `other`.
|
||||
/// Return true if this type is [equivalent to] type `other`.
|
||||
///
|
||||
/// This method returns `false` if either `self` or `other` is not fully static.
|
||||
///
|
||||
/// [equivalent to]: https://typing.readthedocs.io/en/latest/spec/glossary.html#term-equivalent
|
||||
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
||||
if !(self.is_fully_static(db) && other.is_fully_static(db)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO equivalent but not identical structural types, differently-ordered unions and
|
||||
// intersections, other cases?
|
||||
|
||||
// TODO: Once we have support for final classes, we can establish that
|
||||
// `Type::SubclassOf('FinalClass')` is equivalent to `Type::ClassLiteral('FinalClass')`.
|
||||
self == other || matches!((self, other), (Type::Todo(_), Type::Todo(_)))
|
||||
|
||||
// TODO: The following is a workaround that is required to unify the two different versions
|
||||
// of `NoneType` and `NoDefaultType` in typeshed. This should not be required anymore once
|
||||
// we understand `sys.version_info` branches.
|
||||
self == other
|
||||
|| matches!((self, other),
|
||||
(
|
||||
Type::Instance(InstanceType { class: self_class }),
|
||||
Type::Instance(InstanceType { class: target_class })
|
||||
)
|
||||
if {
|
||||
let self_known = self_class.known(db);
|
||||
matches!(self_known, Some(KnownClass::NoneType | KnownClass::NoDefaultType))
|
||||
&& self_known == target_class.known(db)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if both `self` and `other` are the same gradual form
|
||||
/// (limited to `Any`, `Unknown`, or `Todo`).
|
||||
pub(crate) fn is_same_gradual_form(self, other: Type<'db>) -> bool {
|
||||
matches!(
|
||||
(self, other),
|
||||
(Type::Unknown, Type::Unknown)
|
||||
| (Type::Any, Type::Any)
|
||||
| (Type::Todo(_), Type::Todo(_))
|
||||
)
|
||||
}
|
||||
|
||||
/// Return true if this type and `other` have no common elements.
|
||||
@@ -1099,6 +1013,63 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the type does not contain any gradual forms (as a sub-part).
|
||||
pub(crate) fn is_fully_static(self, db: &'db dyn Db) -> bool {
|
||||
match self {
|
||||
Type::Any | Type::Unknown | Type::Todo(_) => false,
|
||||
Type::Never
|
||||
| Type::FunctionLiteral(..)
|
||||
| Type::ModuleLiteral(..)
|
||||
| Type::IntLiteral(_)
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::StringLiteral(_)
|
||||
| Type::LiteralString
|
||||
| Type::BytesLiteral(_)
|
||||
| Type::SliceLiteral(_)
|
||||
| Type::KnownInstance(_) => true,
|
||||
Type::ClassLiteral(_) | Type::SubclassOf(_) | Type::Instance(_) => {
|
||||
// TODO: Ideally, we would iterate over the MRO of the class, check if all
|
||||
// bases are fully static, and only return `true` if that is the case.
|
||||
//
|
||||
// This does not work yet, because we currently infer `Unknown` for some
|
||||
// generic base classes that we don't understand yet. For example, `str`
|
||||
// is defined as `class str(Sequence[str])` in typeshed and we currently
|
||||
// compute its MRO as `(str, Unknown, object)`. This would make us think
|
||||
// that `str` is a gradual type, which causes all sorts of downstream
|
||||
// issues because it does not participate in equivalence/subtyping etc.
|
||||
//
|
||||
// Another problem is that we run into problems if we eagerly query the
|
||||
// MRO of class literals here. I have not fully investigated this, but
|
||||
// iterating over the MRO alone, without even acting on it, causes us to
|
||||
// infer `Unknown` for many classes.
|
||||
|
||||
true
|
||||
}
|
||||
Type::Union(union) => union
|
||||
.elements(db)
|
||||
.iter()
|
||||
.all(|elem| elem.is_fully_static(db)),
|
||||
Type::Intersection(intersection) => {
|
||||
intersection
|
||||
.positive(db)
|
||||
.iter()
|
||||
.all(|elem| elem.is_fully_static(db))
|
||||
&& intersection
|
||||
.negative(db)
|
||||
.iter()
|
||||
.all(|elem| elem.is_fully_static(db))
|
||||
}
|
||||
Type::Tuple(tuple) => tuple
|
||||
.elements(db)
|
||||
.iter()
|
||||
.all(|elem| elem.is_fully_static(db)),
|
||||
// TODO: Once we support them, make sure that we return `false` for other types
|
||||
// containing gradual forms such as `tuple[Any, ...]` or `Callable[..., str]`.
|
||||
// Conversely, make sure to return `true` for homogeneous tuples such as
|
||||
// `tuple[int, ...]`, once we add support for them.
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`
|
||||
@@ -1446,21 +1417,76 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the type of `len()` on a type if it is known more precisely than `int`,
|
||||
/// or `None` otherwise.
|
||||
///
|
||||
/// In the second case, the return type of `len()` in `typeshed` (`int`)
|
||||
/// is used as a fallback.
|
||||
fn len(&self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
fn non_negative_int_literal<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Type<'db>> {
|
||||
match ty {
|
||||
// TODO: Emit diagnostic for non-integers and negative integers
|
||||
Type::IntLiteral(value) => (value >= 0).then_some(ty),
|
||||
Type::BooleanLiteral(value) => Some(Type::IntLiteral(value.into())),
|
||||
Type::Union(union) => {
|
||||
let mut builder = UnionBuilder::new(db);
|
||||
for element in union.elements(db) {
|
||||
builder = builder.add(non_negative_int_literal(db, *element)?);
|
||||
}
|
||||
Some(builder.build())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
let usize_len = match self {
|
||||
Type::BytesLiteral(bytes) => Some(bytes.python_len(db)),
|
||||
Type::StringLiteral(string) => Some(string.python_len(db)),
|
||||
Type::Tuple(tuple) => Some(tuple.len(db)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(usize_len) = usize_len {
|
||||
return usize_len.try_into().ok().map(Type::IntLiteral);
|
||||
}
|
||||
|
||||
let return_ty = match self.call_dunder(db, "__len__", &[*self]) {
|
||||
// TODO: emit a diagnostic
|
||||
CallDunderResult::MethodNotAvailable => return None,
|
||||
|
||||
CallDunderResult::CallOutcome(outcome) | CallDunderResult::PossiblyUnbound(outcome) => {
|
||||
outcome.return_ty(db)?
|
||||
}
|
||||
};
|
||||
|
||||
non_negative_int_literal(db, return_ty)
|
||||
}
|
||||
|
||||
/// Return the outcome of calling an object of this type.
|
||||
#[must_use]
|
||||
fn call(self, db: &'db dyn Db, arg_types: &[Type<'db>]) -> CallOutcome<'db> {
|
||||
match self {
|
||||
// TODO validate typed call arguments vs callable signature
|
||||
Type::FunctionLiteral(function_type) => {
|
||||
if function_type.is_known(db, KnownFunction::RevealType) {
|
||||
CallOutcome::revealed(
|
||||
function_type.signature(db).return_ty,
|
||||
*arg_types.first().unwrap_or(&Type::Unknown),
|
||||
)
|
||||
} else {
|
||||
CallOutcome::callable(function_type.signature(db).return_ty)
|
||||
Type::FunctionLiteral(function_type) => match function_type.known(db) {
|
||||
Some(KnownFunction::RevealType) => CallOutcome::revealed(
|
||||
function_type.signature(db).return_ty,
|
||||
*arg_types.first().unwrap_or(&Type::Unknown),
|
||||
),
|
||||
|
||||
Some(KnownFunction::Len) => {
|
||||
let normal_return_ty = function_type.signature(db).return_ty;
|
||||
|
||||
let [only_arg] = arg_types else {
|
||||
// TODO: Emit a diagnostic
|
||||
return CallOutcome::callable(normal_return_ty);
|
||||
};
|
||||
let len_ty = only_arg.len(db);
|
||||
|
||||
CallOutcome::callable(len_ty.unwrap_or(normal_return_ty))
|
||||
}
|
||||
}
|
||||
|
||||
_ => CallOutcome::callable(function_type.signature(db).return_ty),
|
||||
},
|
||||
|
||||
// TODO annotated return type on `__new__` or metaclass `__call__`
|
||||
Type::ClassLiteral(ClassLiteralType { class }) => {
|
||||
@@ -1509,7 +1535,7 @@ impl<'db> Type<'db> {
|
||||
// `Any` is callable, and its return type is also `Any`.
|
||||
Type::Any => CallOutcome::callable(Type::Any),
|
||||
|
||||
Type::Todo(_) => CallOutcome::callable(todo_type!()),
|
||||
Type::Todo(_) => CallOutcome::callable(todo_type!("call todo")),
|
||||
|
||||
Type::Unknown => CallOutcome::callable(Type::Unknown),
|
||||
|
||||
@@ -1662,6 +1688,8 @@ impl<'db> Type<'db> {
|
||||
Type::KnownInstance(KnownInstanceType::Never | KnownInstanceType::NoReturn) => {
|
||||
Type::Never
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::LiteralString) => Type::LiteralString,
|
||||
Type::KnownInstance(KnownInstanceType::Any) => Type::Any,
|
||||
_ => todo_type!(),
|
||||
}
|
||||
}
|
||||
@@ -1898,8 +1926,8 @@ impl<'db> KnownClass {
|
||||
|
||||
// typing_extensions has a 3.13+ re-export for the `typing.NoDefault`
|
||||
// singleton, but not for `typing._NoDefaultType`. So we need to switch
|
||||
// to `typing.NoDefault` for newer versions:
|
||||
if python_version.major >= 3 && python_version.minor >= 13 {
|
||||
// to `typing._NoDefaultType` for newer versions:
|
||||
if python_version >= PythonVersion::PY313 {
|
||||
CoreStdlibModule::Typing
|
||||
} else {
|
||||
CoreStdlibModule::TypingExtensions
|
||||
@@ -1968,7 +1996,7 @@ impl<'db> KnownClass {
|
||||
}
|
||||
|
||||
/// Return `true` if the module of `self` matches `module_name`
|
||||
fn check_module(self, db: &dyn Db, module: &Module) -> bool {
|
||||
fn check_module(self, db: &'db dyn Db, module: &Module) -> bool {
|
||||
if !module.search_path().is_standard_library() {
|
||||
return false;
|
||||
}
|
||||
@@ -2002,6 +2030,8 @@ impl<'db> KnownClass {
|
||||
pub enum KnownInstanceType<'db> {
|
||||
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
||||
Literal,
|
||||
/// The symbol `typing.LiteralString` (which can also be found as `typing_extensions.LiteralString`)
|
||||
LiteralString,
|
||||
/// The symbol `typing.Optional` (which can also be found as `typing_extensions.Optional`)
|
||||
Optional,
|
||||
/// The symbol `typing.Union` (which can also be found as `typing_extensions.Union`)
|
||||
@@ -2010,6 +2040,8 @@ pub enum KnownInstanceType<'db> {
|
||||
NoReturn,
|
||||
/// The symbol `typing.Never` available since 3.11 (which can also be found as `typing_extensions.Never`)
|
||||
Never,
|
||||
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
|
||||
Any,
|
||||
/// A single instance of `typing.TypeVar`
|
||||
TypeVar(TypeVarInstance<'db>),
|
||||
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
|
||||
@@ -2021,11 +2053,13 @@ impl<'db> KnownInstanceType<'db> {
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Literal => "Literal",
|
||||
Self::LiteralString => "LiteralString",
|
||||
Self::Optional => "Optional",
|
||||
Self::Union => "Union",
|
||||
Self::TypeVar(_) => "TypeVar",
|
||||
Self::NoReturn => "NoReturn",
|
||||
Self::Never => "Never",
|
||||
Self::Any => "Any",
|
||||
Self::TypeAliasType(_) => "TypeAliasType",
|
||||
}
|
||||
}
|
||||
@@ -2034,11 +2068,13 @@ impl<'db> KnownInstanceType<'db> {
|
||||
pub const fn bool(self) -> Truthiness {
|
||||
match self {
|
||||
Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Optional
|
||||
| Self::TypeVar(_)
|
||||
| Self::Union
|
||||
| Self::NoReturn
|
||||
| Self::Never
|
||||
| Self::Any
|
||||
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
|
||||
}
|
||||
}
|
||||
@@ -2047,10 +2083,12 @@ impl<'db> KnownInstanceType<'db> {
|
||||
pub fn repr(self, db: &'db dyn Db) -> &'db str {
|
||||
match self {
|
||||
Self::Literal => "typing.Literal",
|
||||
Self::LiteralString => "typing.LiteralString",
|
||||
Self::Optional => "typing.Optional",
|
||||
Self::Union => "typing.Union",
|
||||
Self::NoReturn => "typing.NoReturn",
|
||||
Self::Never => "typing.Never",
|
||||
Self::Any => "typing.Any",
|
||||
Self::TypeVar(typevar) => typevar.name(db),
|
||||
Self::TypeAliasType(_) => "typing.TypeAliasType",
|
||||
}
|
||||
@@ -2060,10 +2098,12 @@ impl<'db> KnownInstanceType<'db> {
|
||||
pub const fn class(self) -> KnownClass {
|
||||
match self {
|
||||
Self::Literal => KnownClass::SpecialForm,
|
||||
Self::LiteralString => KnownClass::SpecialForm,
|
||||
Self::Optional => KnownClass::SpecialForm,
|
||||
Self::Union => KnownClass::SpecialForm,
|
||||
Self::NoReturn => KnownClass::SpecialForm,
|
||||
Self::Never => KnownClass::SpecialForm,
|
||||
Self::Any => KnownClass::Object,
|
||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||
}
|
||||
@@ -2083,7 +2123,9 @@ impl<'db> KnownInstanceType<'db> {
|
||||
return None;
|
||||
}
|
||||
match (module.name().as_str(), instance_name) {
|
||||
("typing", "Any") => Some(Self::Any),
|
||||
("typing" | "typing_extensions", "Literal") => Some(Self::Literal),
|
||||
("typing" | "typing_extensions", "LiteralString") => Some(Self::LiteralString),
|
||||
("typing" | "typing_extensions", "Optional") => Some(Self::Optional),
|
||||
("typing" | "typing_extensions", "Union") => Some(Self::Union),
|
||||
("typing" | "typing_extensions", "NoReturn") => Some(Self::NoReturn),
|
||||
@@ -2137,7 +2179,7 @@ impl<'db> TypeVarInstance<'db> {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn constraints(self, db: &'db dyn Db) -> Option<&[Type<'db>]> {
|
||||
pub(crate) fn constraints(self, db: &'db dyn Db) -> Option<&'db [Type<'db>]> {
|
||||
if let Some(TypeVarBoundOrConstraints::Constraints(tuple)) = self.bound_or_constraints(db) {
|
||||
Some(tuple.elements(db))
|
||||
} else {
|
||||
@@ -2510,14 +2552,6 @@ impl Truthiness {
|
||||
matches!(self, Truthiness::Ambiguous)
|
||||
}
|
||||
|
||||
const fn is_always_false(self) -> bool {
|
||||
matches!(self, Truthiness::AlwaysFalse)
|
||||
}
|
||||
|
||||
const fn is_always_true(self) -> bool {
|
||||
matches!(self, Truthiness::AlwaysTrue)
|
||||
}
|
||||
|
||||
const fn negate(self) -> Self {
|
||||
match self {
|
||||
Self::AlwaysTrue => Self::AlwaysFalse,
|
||||
@@ -2626,13 +2660,15 @@ pub enum KnownFunction {
|
||||
ConstraintFunction(KnownConstraintFunction),
|
||||
/// `builtins.reveal_type`, `typing.reveal_type` or `typing_extensions.reveal_type`
|
||||
RevealType,
|
||||
/// `builtins.len`
|
||||
Len,
|
||||
}
|
||||
|
||||
impl KnownFunction {
|
||||
pub fn constraint_function(self) -> Option<KnownConstraintFunction> {
|
||||
match self {
|
||||
Self::ConstraintFunction(f) => Some(f),
|
||||
Self::RevealType => None,
|
||||
Self::RevealType | Self::Len => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2649,6 +2685,7 @@ impl KnownFunction {
|
||||
"issubclass" if definition.is_builtin_definition(db) => Some(
|
||||
KnownFunction::ConstraintFunction(KnownConstraintFunction::IsSubclass),
|
||||
),
|
||||
"len" if definition.is_builtin_definition(db) => Some(KnownFunction::Len),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -2689,7 +2726,7 @@ impl<'db> Class<'db> {
|
||||
///
|
||||
/// Were this not a salsa query, then the calling query
|
||||
/// would depend on the class's AST and rerun for every change in that file.
|
||||
fn explicit_bases(self, db: &'db dyn Db) -> &[Type<'db>] {
|
||||
fn explicit_bases(self, db: &'db dyn Db) -> &'db [Type<'db>] {
|
||||
self.explicit_bases_query(db)
|
||||
}
|
||||
|
||||
@@ -2758,7 +2795,11 @@ impl<'db> Class<'db> {
|
||||
pub fn is_subclass_of(self, db: &'db dyn Db, other: Class) -> bool {
|
||||
// `is_subclass_of` is checking the subtype relation, in which gradual types do not
|
||||
// participate, so we should not return `True` if we find `Any/Unknown` in the MRO.
|
||||
self.iter_mro(db).contains(&ClassBase::Class(other))
|
||||
self.is_subclass_of_base(db, other)
|
||||
}
|
||||
|
||||
fn is_subclass_of_base(self, db: &'db dyn Db, other: impl Into<ClassBase<'db>>) -> bool {
|
||||
self.iter_mro(db).contains(&other.into())
|
||||
}
|
||||
|
||||
/// Return the explicit `metaclass` of this class, if one is defined.
|
||||
@@ -3099,8 +3140,9 @@ pub struct StringLiteralType<'db> {
|
||||
}
|
||||
|
||||
impl<'db> StringLiteralType<'db> {
|
||||
pub fn len(&self, db: &'db dyn Db) -> usize {
|
||||
self.value(db).len()
|
||||
/// The length of the string, as would be returned by Python's `len()`.
|
||||
pub fn python_len(&self, db: &'db dyn Db) -> usize {
|
||||
self.value(db).chars().count()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3110,6 +3152,12 @@ pub struct BytesLiteralType<'db> {
|
||||
value: Box<[u8]>,
|
||||
}
|
||||
|
||||
impl<'db> BytesLiteralType<'db> {
|
||||
pub fn python_len(&self, db: &'db dyn Db) -> usize {
|
||||
self.value(db).len()
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::interned]
|
||||
pub struct SliceLiteralType<'db> {
|
||||
start: Option<i32>,
|
||||
@@ -3117,7 +3165,7 @@ pub struct SliceLiteralType<'db> {
|
||||
step: Option<i32>,
|
||||
}
|
||||
|
||||
impl<'db> SliceLiteralType<'db> {
|
||||
impl SliceLiteralType<'_> {
|
||||
fn as_tuple(self, db: &dyn Db) -> (Option<i32>, Option<i32>, Option<i32>) {
|
||||
(self.start(db), self.stop(db), self.step(db))
|
||||
}
|
||||
@@ -3129,6 +3177,23 @@ pub struct TupleType<'db> {
|
||||
}
|
||||
|
||||
impl<'db> TupleType<'db> {
|
||||
pub fn from_elements<T: Into<Type<'db>>>(
|
||||
db: &'db dyn Db,
|
||||
types: impl IntoIterator<Item = T>,
|
||||
) -> Type<'db> {
|
||||
let mut elements = vec![];
|
||||
|
||||
for ty in types {
|
||||
let ty = ty.into();
|
||||
if ty.is_never() {
|
||||
return Type::Never;
|
||||
}
|
||||
elements.push(ty);
|
||||
}
|
||||
|
||||
Type::Tuple(Self::new(db, elements.into_boxed_slice()))
|
||||
}
|
||||
|
||||
pub fn get(&self, db: &'db dyn Db, index: usize) -> Option<Type<'db>> {
|
||||
self.elements(db).get(index).copied()
|
||||
}
|
||||
@@ -3146,46 +3211,20 @@ static_assertions::assert_eq_size!(Type, [u8; 16]);
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::db::tests::{setup_db, TestDb, TestDbBuilder};
|
||||
use crate::stdlib::typing_symbol;
|
||||
use crate::ProgramSettings;
|
||||
use crate::PythonVersion;
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
use ruff_db::system::DbWithTestSystem;
|
||||
use ruff_db::testing::assert_function_query_was_not_run;
|
||||
use ruff_python_ast as ast;
|
||||
use test_case::test_case;
|
||||
|
||||
pub(crate) fn setup_db_with_python_version(python_version: PythonVersion) -> TestDb {
|
||||
let db = TestDb::new();
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&src_root)
|
||||
.unwrap();
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: python_version,
|
||||
search_paths: SearchPathSettings::new(src_root),
|
||||
},
|
||||
)
|
||||
.expect("Valid search path settings");
|
||||
|
||||
db
|
||||
}
|
||||
|
||||
pub(crate) fn setup_db() -> TestDb {
|
||||
setup_db_with_python_version(PythonVersion::default())
|
||||
}
|
||||
|
||||
/// A test representation of a type that can be transformed unambiguously into a real Type,
|
||||
/// given a db.
|
||||
#[derive(Debug, Clone)]
|
||||
enum Ty {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum Ty {
|
||||
Never,
|
||||
Unknown,
|
||||
None,
|
||||
@@ -3209,7 +3248,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
impl Ty {
|
||||
fn into_type(self, db: &TestDb) -> Type<'_> {
|
||||
pub(crate) fn into_type(self, db: &TestDb) -> Type<'_> {
|
||||
match self {
|
||||
Ty::Never => Type::Never,
|
||||
Ty::Unknown => Type::Unknown,
|
||||
@@ -3240,13 +3279,21 @@ pub(crate) mod tests {
|
||||
builder.build()
|
||||
}
|
||||
Ty::Tuple(tys) => {
|
||||
let elements: Vec<Type> = tys.into_iter().map(|ty| ty.into_type(db)).collect();
|
||||
Type::tuple(db, &elements)
|
||||
let elements = tys.into_iter().map(|ty| ty.into_type(db));
|
||||
Type::tuple(db, elements)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test_case(Ty::Tuple(vec![Ty::Never]))]
|
||||
#[test_case(Ty::Tuple(vec![Ty::BuiltinInstance("str"), Ty::Never, Ty::BuiltinInstance("int")]))]
|
||||
#[test_case(Ty::Tuple(vec![Ty::Tuple(vec![Ty::Never])]))]
|
||||
fn tuple_containing_never_simplifies_to_never(ty: Ty) {
|
||||
let db = setup_db();
|
||||
assert_eq!(ty.into_type(&db), Type::Never);
|
||||
}
|
||||
|
||||
#[test_case(Ty::BuiltinInstance("str"), Ty::BuiltinInstance("object"))]
|
||||
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("object"))]
|
||||
#[test_case(Ty::Unknown, Ty::IntLiteral(1))]
|
||||
@@ -3339,7 +3386,9 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[test_case(Ty::BuiltinInstance("object"), Ty::BuiltinInstance("int"))]
|
||||
#[test_case(Ty::Unknown, Ty::Unknown)]
|
||||
#[test_case(Ty::Unknown, Ty::IntLiteral(1))]
|
||||
#[test_case(Ty::Any, Ty::Any)]
|
||||
#[test_case(Ty::Any, Ty::IntLiteral(1))]
|
||||
#[test_case(Ty::IntLiteral(1), Ty::Unknown)]
|
||||
#[test_case(Ty::IntLiteral(1), Ty::Any)]
|
||||
@@ -3360,6 +3409,7 @@ pub(crate) mod tests {
|
||||
#[test_case(Ty::IntLiteral(1), Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(1)]})]
|
||||
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinClassLiteral("object"))]
|
||||
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinClassLiteral("int"))]
|
||||
#[test_case(Ty::TypingInstance("_SpecialForm"), Ty::TypingLiteral)]
|
||||
fn is_not_subtype_of(from: Ty, to: Ty) {
|
||||
let db = setup_db();
|
||||
assert!(!from.into_type(&db).is_subtype_of(&db, to.into_type(&db)));
|
||||
@@ -3446,6 +3496,18 @@ pub(crate) mod tests {
|
||||
assert!(from.into_type(&db).is_equivalent_to(&db, to.into_type(&db)));
|
||||
}
|
||||
|
||||
#[test_case(Ty::Any, Ty::Any)]
|
||||
#[test_case(Ty::Any, Ty::None)]
|
||||
#[test_case(Ty::Unknown, Ty::Unknown)]
|
||||
#[test_case(Ty::Todo, Ty::Todo)]
|
||||
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(0)]))]
|
||||
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2), Ty::IntLiteral(3)]))]
|
||||
fn is_not_equivalent_to(from: Ty, to: Ty) {
|
||||
let db = setup_db();
|
||||
|
||||
assert!(!from.into_type(&db).is_equivalent_to(&db, to.into_type(&db)));
|
||||
}
|
||||
|
||||
#[test_case(Ty::Never, Ty::Never)]
|
||||
#[test_case(Ty::Never, Ty::None)]
|
||||
#[test_case(Ty::Never, Ty::BuiltinInstance("int"))]
|
||||
@@ -3635,11 +3697,16 @@ pub(crate) mod tests {
|
||||
assert!(from.into_type(&db).is_singleton(&db));
|
||||
}
|
||||
|
||||
/// TODO: test documentation
|
||||
/// Explicitly test for Python version <3.13 and >=3.13, to ensure that
|
||||
/// the fallback to `typing_extensions` is working correctly.
|
||||
/// See [`KnownClass::canonical_module`] for more information.
|
||||
#[test_case(PythonVersion::PY312)]
|
||||
#[test_case(PythonVersion::PY313)]
|
||||
fn no_default_type_is_singleton(python_version: PythonVersion) {
|
||||
let db = setup_db_with_python_version(python_version);
|
||||
let db = TestDbBuilder::new()
|
||||
.with_python_version(python_version)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let no_default = Ty::KnownClassInstance(KnownClass::NoDefaultType).into_type(&db);
|
||||
|
||||
@@ -3684,6 +3751,41 @@ pub(crate) mod tests {
|
||||
assert!(!from.into_type(&db).is_singleton(&db));
|
||||
}
|
||||
|
||||
#[test_case(Ty::Never)]
|
||||
#[test_case(Ty::None)]
|
||||
#[test_case(Ty::IntLiteral(1))]
|
||||
#[test_case(Ty::BooleanLiteral(true))]
|
||||
#[test_case(Ty::StringLiteral("abc"))]
|
||||
#[test_case(Ty::LiteralString)]
|
||||
#[test_case(Ty::BytesLiteral("abc"))]
|
||||
#[test_case(Ty::KnownClassInstance(KnownClass::Str))]
|
||||
#[test_case(Ty::KnownClassInstance(KnownClass::Object))]
|
||||
#[test_case(Ty::KnownClassInstance(KnownClass::Type))]
|
||||
#[test_case(Ty::BuiltinClassLiteral("str"))]
|
||||
#[test_case(Ty::TypingLiteral)]
|
||||
#[test_case(Ty::Union(vec![Ty::KnownClassInstance(KnownClass::Str), Ty::None]))]
|
||||
#[test_case(Ty::Intersection{pos: vec![Ty::KnownClassInstance(KnownClass::Str)], neg: vec![Ty::LiteralString]})]
|
||||
#[test_case(Ty::Tuple(vec![]))]
|
||||
#[test_case(Ty::Tuple(vec![Ty::KnownClassInstance(KnownClass::Int), Ty::KnownClassInstance(KnownClass::Object)]))]
|
||||
fn is_fully_static(from: Ty) {
|
||||
let db = setup_db();
|
||||
|
||||
assert!(from.into_type(&db).is_fully_static(&db));
|
||||
}
|
||||
|
||||
#[test_case(Ty::Any)]
|
||||
#[test_case(Ty::Unknown)]
|
||||
#[test_case(Ty::Todo)]
|
||||
#[test_case(Ty::Union(vec![Ty::Any, Ty::KnownClassInstance(KnownClass::Str)]))]
|
||||
#[test_case(Ty::Union(vec![Ty::KnownClassInstance(KnownClass::Str), Ty::Unknown]))]
|
||||
#[test_case(Ty::Intersection{pos: vec![Ty::Any], neg: vec![Ty::LiteralString]})]
|
||||
#[test_case(Ty::Tuple(vec![Ty::KnownClassInstance(KnownClass::Int), Ty::Any]))]
|
||||
fn is_not_fully_static(from: Ty) {
|
||||
let db = setup_db();
|
||||
|
||||
assert!(!from.into_type(&db).is_fully_static(&db));
|
||||
}
|
||||
|
||||
#[test_case(Ty::IntLiteral(1); "is_int_literal_truthy")]
|
||||
#[test_case(Ty::IntLiteral(-1))]
|
||||
#[test_case(Ty::StringLiteral("foo"))]
|
||||
@@ -3738,7 +3840,10 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn typing_vs_typeshed_no_default() {
|
||||
let db = setup_db();
|
||||
let db = TestDbBuilder::new()
|
||||
.with_python_version(PythonVersion::PY313)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let typing_no_default = typing_symbol(&db, "NoDefault").expect_type();
|
||||
let typing_extensions_no_default = typing_extensions_symbol(&db, "NoDefault").expect_type();
|
||||
@@ -3858,19 +3963,6 @@ pub(crate) mod tests {
|
||||
let todo3 = todo_type!();
|
||||
let todo4 = todo_type!();
|
||||
|
||||
assert!(todo1.is_equivalent_to(&db, todo2));
|
||||
assert!(todo3.is_equivalent_to(&db, todo4));
|
||||
assert!(todo1.is_equivalent_to(&db, todo3));
|
||||
|
||||
assert!(todo1.is_subtype_of(&db, todo2));
|
||||
assert!(todo2.is_subtype_of(&db, todo1));
|
||||
|
||||
assert!(todo3.is_subtype_of(&db, todo4));
|
||||
assert!(todo4.is_subtype_of(&db, todo3));
|
||||
|
||||
assert!(todo1.is_subtype_of(&db, todo3));
|
||||
assert!(todo3.is_subtype_of(&db, todo1));
|
||||
|
||||
let int = KnownClass::Int.to_instance(&db);
|
||||
|
||||
assert!(int.is_assignable_to(&db, todo1));
|
||||
|
||||
@@ -73,7 +73,8 @@ impl<'db> UnionBuilder<'db> {
|
||||
// supertype of bool. Therefore, we are done.
|
||||
break;
|
||||
}
|
||||
if ty.is_subtype_of(self.db, *element) {
|
||||
|
||||
if ty.is_same_gradual_form(*element) || ty.is_subtype_of(self.db, *element) {
|
||||
return self;
|
||||
} else if element.is_subtype_of(self.db, ty) {
|
||||
to_remove.push(index);
|
||||
@@ -259,7 +260,9 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||
let mut to_remove = SmallVec::<[usize; 1]>::new();
|
||||
for (index, existing_positive) in self.positive.iter().enumerate() {
|
||||
// S & T = S if S <: T
|
||||
if existing_positive.is_subtype_of(db, new_positive) {
|
||||
if existing_positive.is_subtype_of(db, new_positive)
|
||||
|| existing_positive.is_same_gradual_form(new_positive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// same rule, reverse order
|
||||
@@ -375,36 +378,14 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{IntersectionBuilder, IntersectionType, Type, UnionType};
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::stdlib::typing_symbol;
|
||||
|
||||
use crate::db::tests::{setup_db, TestDb};
|
||||
use crate::types::{global_symbol, todo_type, KnownClass, UnionBuilder};
|
||||
use crate::ProgramSettings;
|
||||
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
use ruff_db::system::DbWithTestSystem;
|
||||
use test_case::test_case;
|
||||
|
||||
fn setup_db() -> TestDb {
|
||||
let db = TestDb::new();
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&src_root)
|
||||
.unwrap();
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: PythonVersion::default(),
|
||||
search_paths: SearchPathSettings::new(src_root),
|
||||
},
|
||||
)
|
||||
.expect("Valid search path settings");
|
||||
|
||||
db
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_union() {
|
||||
let db = setup_db();
|
||||
@@ -497,6 +478,17 @@ mod tests {
|
||||
assert_eq!(u1.expect_union().elements(&db), &[t1, t0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_union_simplify_multiple_unknown() {
|
||||
let db = setup_db();
|
||||
let t0 = KnownClass::Str.to_instance(&db);
|
||||
let t1 = Type::Unknown;
|
||||
|
||||
let u = UnionType::from_elements(&db, [t0, t1, t1]);
|
||||
|
||||
assert_eq!(u.expect_union().elements(&db), &[t0, t1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_union_subsume_multiple() {
|
||||
let db = setup_db();
|
||||
@@ -604,6 +596,42 @@ mod tests {
|
||||
assert_eq!(ty, Type::Never);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_intersection_simplify_multiple_unknown() {
|
||||
let db = setup_db();
|
||||
|
||||
let ty = IntersectionBuilder::new(&db)
|
||||
.add_positive(Type::Unknown)
|
||||
.add_positive(Type::Unknown)
|
||||
.build();
|
||||
assert_eq!(ty, Type::Unknown);
|
||||
|
||||
let ty = IntersectionBuilder::new(&db)
|
||||
.add_positive(Type::Unknown)
|
||||
.add_negative(Type::Unknown)
|
||||
.build();
|
||||
assert_eq!(ty, Type::Unknown);
|
||||
|
||||
let ty = IntersectionBuilder::new(&db)
|
||||
.add_negative(Type::Unknown)
|
||||
.add_negative(Type::Unknown)
|
||||
.build();
|
||||
assert_eq!(ty, Type::Unknown);
|
||||
|
||||
let ty = IntersectionBuilder::new(&db)
|
||||
.add_positive(Type::Unknown)
|
||||
.add_positive(Type::IntLiteral(0))
|
||||
.add_negative(Type::Unknown)
|
||||
.build();
|
||||
assert_eq!(
|
||||
ty,
|
||||
IntersectionBuilder::new(&db)
|
||||
.add_positive(Type::Unknown)
|
||||
.add_positive(Type::IntLiteral(0))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intersection_distributes_over_union() {
|
||||
let db = setup_db();
|
||||
@@ -626,59 +654,85 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn intersection_negation_distributes_over_union() {
|
||||
let db = setup_db();
|
||||
let st = typing_symbol(&db, "Sized").expect_type().to_instance(&db);
|
||||
let ht = typing_symbol(&db, "Hashable")
|
||||
let mut db = setup_db();
|
||||
db.write_dedented(
|
||||
"/src/module.py",
|
||||
r#"
|
||||
class A: ...
|
||||
class B: ...
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap();
|
||||
|
||||
let a = global_symbol(&db, module, "A")
|
||||
.expect_type()
|
||||
.to_instance(&db);
|
||||
// sh_t: Sized & Hashable
|
||||
let sh_t = IntersectionBuilder::new(&db)
|
||||
.add_positive(st)
|
||||
.add_positive(ht)
|
||||
let b = global_symbol(&db, module, "B")
|
||||
.expect_type()
|
||||
.to_instance(&db);
|
||||
|
||||
// intersection: A & B
|
||||
let intersection = IntersectionBuilder::new(&db)
|
||||
.add_positive(a)
|
||||
.add_positive(b)
|
||||
.build()
|
||||
.expect_intersection();
|
||||
assert_eq!(sh_t.pos_vec(&db), &[st, ht]);
|
||||
assert_eq!(sh_t.neg_vec(&db), &[]);
|
||||
assert_eq!(intersection.pos_vec(&db), &[a, b]);
|
||||
assert_eq!(intersection.neg_vec(&db), &[]);
|
||||
|
||||
// ~sh_t => ~Sized | ~Hashable
|
||||
let not_s_h_t = IntersectionBuilder::new(&db)
|
||||
.add_negative(Type::Intersection(sh_t))
|
||||
// ~intersection => ~A | ~B
|
||||
let negated_intersection = IntersectionBuilder::new(&db)
|
||||
.add_negative(Type::Intersection(intersection))
|
||||
.build()
|
||||
.expect_union();
|
||||
|
||||
// should have as elements: (~Sized),(~Hashable)
|
||||
let not_st = st.negate(&db);
|
||||
let not_ht = ht.negate(&db);
|
||||
assert_eq!(not_s_h_t.elements(&db), &[not_st, not_ht]);
|
||||
// should have as elements ~A and ~B
|
||||
let not_a = a.negate(&db);
|
||||
let not_b = b.negate(&db);
|
||||
assert_eq!(negated_intersection.elements(&db), &[not_a, not_b]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_intersection_negation_distributes_over_union() {
|
||||
let db = setup_db();
|
||||
let it = KnownClass::Int.to_instance(&db);
|
||||
let st = typing_symbol(&db, "Sized").expect_type().to_instance(&db);
|
||||
let ht = typing_symbol(&db, "Hashable")
|
||||
let mut db = setup_db();
|
||||
db.write_dedented(
|
||||
"/src/module.py",
|
||||
r#"
|
||||
class A: ...
|
||||
class B: ...
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap();
|
||||
|
||||
let a = global_symbol(&db, module, "A")
|
||||
.expect_type()
|
||||
.to_instance(&db);
|
||||
// s_not_h_t: Sized & ~Hashable
|
||||
let s_not_h_t = IntersectionBuilder::new(&db)
|
||||
.add_positive(st)
|
||||
.add_negative(ht)
|
||||
let b = global_symbol(&db, module, "B")
|
||||
.expect_type()
|
||||
.to_instance(&db);
|
||||
let int = KnownClass::Int.to_instance(&db);
|
||||
|
||||
// a_not_b: A & ~B
|
||||
let a_not_b = IntersectionBuilder::new(&db)
|
||||
.add_positive(a)
|
||||
.add_negative(b)
|
||||
.build()
|
||||
.expect_intersection();
|
||||
assert_eq!(s_not_h_t.pos_vec(&db), &[st]);
|
||||
assert_eq!(s_not_h_t.neg_vec(&db), &[ht]);
|
||||
assert_eq!(a_not_b.pos_vec(&db), &[a]);
|
||||
assert_eq!(a_not_b.neg_vec(&db), &[b]);
|
||||
|
||||
// let's build int & ~(Sized & ~Hashable)
|
||||
let tt = IntersectionBuilder::new(&db)
|
||||
.add_positive(it)
|
||||
.add_negative(Type::Intersection(s_not_h_t))
|
||||
// let's build
|
||||
// int & ~(A & ~B)
|
||||
// = int & ~(A & ~B)
|
||||
// = int & (~A | B)
|
||||
// = (int & ~A) | (int & B)
|
||||
let t = IntersectionBuilder::new(&db)
|
||||
.add_positive(int)
|
||||
.add_negative(Type::Intersection(a_not_b))
|
||||
.build();
|
||||
|
||||
// int & ~(Sized & ~Hashable)
|
||||
// -> int & (~Sized | Hashable)
|
||||
// -> (int & ~Sized) | (int & Hashable)
|
||||
assert_eq!(tt.display(&db).to_string(), "int & ~Sized | int & Hashable");
|
||||
assert_eq!(t.display(&db).to_string(), "int & ~A | int & B");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -289,7 +289,7 @@ struct DisplayMaybeNegatedType<'db> {
|
||||
negated: bool,
|
||||
}
|
||||
|
||||
impl<'db> Display for DisplayMaybeNegatedType<'db> {
|
||||
impl Display for DisplayMaybeNegatedType<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.negated {
|
||||
f.write_str("~")?;
|
||||
@@ -319,7 +319,7 @@ pub(crate) struct DisplayTypeArray<'b, 'db> {
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl<'db> Display for DisplayTypeArray<'_, 'db> {
|
||||
impl Display for DisplayTypeArray<'_, '_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.join(", ")
|
||||
.entries(self.types.iter().map(|ty| ty.display(self.db)))
|
||||
@@ -357,31 +357,10 @@ impl Display for DisplayStringLiteralType<'_> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
use ruff_db::system::DbWithTestSystem;
|
||||
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::db::tests::setup_db;
|
||||
use crate::types::{global_symbol, SliceLiteralType, StringLiteralType, Type, UnionType};
|
||||
use crate::{Program, ProgramSettings, PythonVersion, SearchPathSettings};
|
||||
|
||||
fn setup_db() -> TestDb {
|
||||
let db = TestDb::new();
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&src_root)
|
||||
.unwrap();
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: PythonVersion::default(),
|
||||
search_paths: SearchPathSettings::new(src_root),
|
||||
},
|
||||
)
|
||||
.expect("Valid search path settings");
|
||||
|
||||
db
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_condense_literal_display_by_type() -> anyhow::Result<()> {
|
||||
|
||||
@@ -1660,7 +1660,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let value_ty = self.expression_ty(value);
|
||||
let name_ast_id = name.scoped_expression_id(self.db, self.scope());
|
||||
|
||||
let target_ty = match assignment.target() {
|
||||
let mut target_ty = match assignment.target() {
|
||||
TargetKind::Sequence(unpack) => {
|
||||
let unpacked = infer_unpack_types(self.db, unpack);
|
||||
// Only copy the diagnostics if this is the first assignment to avoid duplicating the
|
||||
@@ -1674,6 +1674,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
TargetKind::Name => value_ty,
|
||||
};
|
||||
|
||||
if let Some(known_instance) = file_to_module(self.db, definition.file(self.db))
|
||||
.as_ref()
|
||||
.and_then(|module| KnownInstanceType::try_from_module_and_symbol(module, &name.id))
|
||||
{
|
||||
target_ty = Type::KnownInstance(known_instance);
|
||||
}
|
||||
|
||||
self.store_expression_type(name, target_ty);
|
||||
self.add_binding(name.into(), definition, target_ty);
|
||||
}
|
||||
@@ -1893,12 +1900,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
is_async: _,
|
||||
} = for_statement;
|
||||
|
||||
self.infer_standalone_expression(iter);
|
||||
|
||||
// TODO more complex assignment targets
|
||||
if let ast::Expr::Name(name) = &**target {
|
||||
self.infer_definition(name);
|
||||
} else {
|
||||
self.infer_standalone_expression(iter);
|
||||
self.infer_expression(target);
|
||||
}
|
||||
self.infer_body(body);
|
||||
@@ -4537,7 +4543,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) {
|
||||
todo_type!()
|
||||
} else {
|
||||
Type::tuple(self.db, &[single_element_ty])
|
||||
Type::tuple(self.db, [single_element_ty])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4642,6 +4648,18 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
);
|
||||
Type::Unknown
|
||||
}
|
||||
KnownInstanceType::LiteralString => {
|
||||
self.diagnostics.add(
|
||||
subscript.into(),
|
||||
"invalid-type-parameter",
|
||||
format_args!(
|
||||
"Type `{}` expected no type parameter. Did you mean to use `Literal[...]` instead?",
|
||||
known_instance.repr(self.db)
|
||||
),
|
||||
);
|
||||
Type::Unknown
|
||||
}
|
||||
KnownInstanceType::Any => Type::Any,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5027,73 +5045,19 @@ fn perform_membership_test_comparison<'db>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::{self, PythonVersion};
|
||||
use crate::db::tests::{setup_db, TestDb, TestDbBuilder};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::symbol::FileScopeId;
|
||||
use crate::semantic_index::{global_scope, semantic_index, symbol_table, use_def_map};
|
||||
use crate::types::check_types;
|
||||
use crate::{HasTy, ProgramSettings, SemanticModel};
|
||||
use crate::{HasTy, SemanticModel};
|
||||
use ruff_db::files::{system_path_to_file, File};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
use ruff_db::system::DbWithTestSystem;
|
||||
use ruff_db::testing::assert_function_query_was_not_run;
|
||||
use test_case::test_case;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn setup_db_with_python_version(python_version: PythonVersion) -> TestDb {
|
||||
let db = TestDb::new();
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&src_root)
|
||||
.unwrap();
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: python_version,
|
||||
search_paths: SearchPathSettings::new(src_root),
|
||||
},
|
||||
)
|
||||
.expect("Valid search path settings");
|
||||
|
||||
db
|
||||
}
|
||||
|
||||
fn setup_db() -> TestDb {
|
||||
setup_db_with_python_version(PythonVersion::default())
|
||||
}
|
||||
|
||||
fn setup_db_with_custom_typeshed<'a>(
|
||||
typeshed: &str,
|
||||
files: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
) -> anyhow::Result<TestDb> {
|
||||
let mut db = TestDb::new();
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
|
||||
db.write_files(files)
|
||||
.context("Failed to write test files")?;
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: PythonVersion::default(),
|
||||
search_paths: SearchPathSettings {
|
||||
custom_typeshed: Some(SystemPathBuf::from(typeshed)),
|
||||
..SearchPathSettings::new(src_root)
|
||||
},
|
||||
},
|
||||
)
|
||||
.context("Failed to create Program")?;
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_public_ty(db: &TestDb, file_name: &str, symbol_name: &str, expected: &str) {
|
||||
let file = system_path_to_file(db, file_name).expect("file to exist");
|
||||
@@ -5340,10 +5304,9 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(PythonVersion::PY39, "ellipsis")]
|
||||
#[test_case(PythonVersion::PY310, "EllipsisType")]
|
||||
fn ellipsis_type(version: PythonVersion, expected_type: &str) -> anyhow::Result<()> {
|
||||
let mut db = setup_db_with_python_version(version);
|
||||
#[test]
|
||||
fn ellipsis_type() -> anyhow::Result<()> {
|
||||
let mut db = setup_db();
|
||||
|
||||
db.write_dedented(
|
||||
"src/a.py",
|
||||
@@ -5352,7 +5315,8 @@ mod tests {
|
||||
",
|
||||
)?;
|
||||
|
||||
assert_public_ty(&db, "src/a.py", "x", expected_type);
|
||||
// TODO: sys.version_info
|
||||
assert_public_ty(&db, "src/a.py", "x", "EllipsisType | ellipsis");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -5488,17 +5452,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn builtin_symbol_custom_stdlib() -> anyhow::Result<()> {
|
||||
let db = setup_db_with_custom_typeshed(
|
||||
"/typeshed",
|
||||
[
|
||||
("/src/a.py", "c = copyright"),
|
||||
(
|
||||
"/typeshed/stdlib/builtins.pyi",
|
||||
"def copyright() -> None: ...",
|
||||
),
|
||||
("/typeshed/stdlib/VERSIONS", "builtins: 3.8-"),
|
||||
],
|
||||
)?;
|
||||
let db = TestDbBuilder::new()
|
||||
.with_custom_typeshed("/typeshed")
|
||||
.with_file("/src/a.py", "c = copyright")
|
||||
.with_file(
|
||||
"/typeshed/stdlib/builtins.pyi",
|
||||
"def copyright() -> None: ...",
|
||||
)
|
||||
.with_file("/typeshed/stdlib/VERSIONS", "builtins: 3.8-")
|
||||
.build()?;
|
||||
|
||||
assert_public_ty(&db, "/src/a.py", "c", "Literal[copyright]");
|
||||
|
||||
@@ -5507,14 +5469,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn unknown_builtin_later_defined() -> anyhow::Result<()> {
|
||||
let db = setup_db_with_custom_typeshed(
|
||||
"/typeshed",
|
||||
[
|
||||
("/src/a.py", "x = foo"),
|
||||
("/typeshed/stdlib/builtins.pyi", "foo = bar; bar = 1"),
|
||||
("/typeshed/stdlib/VERSIONS", "builtins: 3.8-"),
|
||||
],
|
||||
)?;
|
||||
let db = TestDbBuilder::new()
|
||||
.with_custom_typeshed("/typeshed")
|
||||
.with_file("/src/a.py", "x = foo")
|
||||
.with_file("/typeshed/stdlib/builtins.pyi", "foo = bar; bar = 1")
|
||||
.with_file("/typeshed/stdlib/VERSIONS", "builtins: 3.8-")
|
||||
.build()?;
|
||||
|
||||
assert_public_ty(&db, "/src/a.py", "x", "Unknown");
|
||||
|
||||
|
||||
@@ -374,10 +374,12 @@ impl<'db> ClassBase<'db> {
|
||||
KnownInstanceType::TypeVar(_)
|
||||
| KnownInstanceType::TypeAliasType(_)
|
||||
| KnownInstanceType::Literal
|
||||
| KnownInstanceType::LiteralString
|
||||
| KnownInstanceType::Union
|
||||
| KnownInstanceType::NoReturn
|
||||
| KnownInstanceType::Never
|
||||
| KnownInstanceType::Optional => None,
|
||||
KnownInstanceType::Any => Some(Self::Any),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -405,6 +407,12 @@ impl<'db> ClassBase<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<Class<'db>> for ClassBase<'db> {
|
||||
fn from(value: Class<'db>) -> Self {
|
||||
ClassBase::Class(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<ClassBase<'db>> for Type<'db> {
|
||||
fn from(value: ClassBase<'db>) -> Self {
|
||||
match value {
|
||||
|
||||
@@ -54,6 +54,7 @@ pub(crate) fn narrowing_constraint<'db>(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::ref_option)]
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn all_narrowing_constraints_for_pattern<'db>(
|
||||
db: &'db dyn Db,
|
||||
@@ -62,6 +63,7 @@ fn all_narrowing_constraints_for_pattern<'db>(
|
||||
NarrowingConstraintsBuilder::new(db, ConstraintNode::Pattern(pattern), true).finish()
|
||||
}
|
||||
|
||||
#[allow(clippy::ref_option)]
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn all_narrowing_constraints_for_expression<'db>(
|
||||
db: &'db dyn Db,
|
||||
@@ -70,6 +72,7 @@ fn all_narrowing_constraints_for_expression<'db>(
|
||||
NarrowingConstraintsBuilder::new(db, ConstraintNode::Expression(expression), true).finish()
|
||||
}
|
||||
|
||||
#[allow(clippy::ref_option)]
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn all_negative_narrowing_constraints_for_expression<'db>(
|
||||
db: &'db dyn Db,
|
||||
@@ -291,8 +294,15 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||
.chain(comparators)
|
||||
.tuple_windows::<(&ruff_python_ast::Expr, &ruff_python_ast::Expr)>();
|
||||
let mut constraints = NarrowingConstraints::default();
|
||||
|
||||
let mut last_rhs_ty: Option<Type> = None;
|
||||
|
||||
for (op, (left, right)) in std::iter::zip(&**ops, comparator_tuples) {
|
||||
let lhs_ty = last_rhs_ty.unwrap_or_else(|| {
|
||||
inference.expression_ty(left.scoped_expression_id(self.db, scope))
|
||||
});
|
||||
let rhs_ty = inference.expression_ty(right.scoped_expression_id(self.db, scope));
|
||||
last_rhs_ty = Some(rhs_ty);
|
||||
|
||||
match left {
|
||||
ast::Expr::Name(ast::ExprName {
|
||||
@@ -327,6 +337,9 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||
constraints.insert(symbol, ty);
|
||||
}
|
||||
}
|
||||
ast::CmpOp::Eq if lhs_ty.is_literal_string() => {
|
||||
constraints.insert(symbol, rhs_ty);
|
||||
}
|
||||
_ => {
|
||||
// TODO other comparison types
|
||||
}
|
||||
@@ -385,46 +398,58 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||
let scope = self.scope();
|
||||
let inference = infer_expression_types(self.db, expression);
|
||||
|
||||
let callable_ty =
|
||||
inference.expression_ty(expr_call.func.scoped_expression_id(self.db, scope));
|
||||
|
||||
// TODO: add support for PEP 604 union types on the right hand side of `isinstance`
|
||||
// and `issubclass`, for example `isinstance(x, str | (int | float))`.
|
||||
match inference
|
||||
.expression_ty(expr_call.func.scoped_expression_id(self.db, scope))
|
||||
.into_function_literal()
|
||||
.and_then(|f| f.known(self.db))
|
||||
.and_then(KnownFunction::constraint_function)
|
||||
{
|
||||
Some(function) if expr_call.arguments.keywords.is_empty() => {
|
||||
if let [ast::Expr::Name(ast::ExprName { id, .. }), class_info] =
|
||||
match callable_ty {
|
||||
Type::FunctionLiteral(function_type) if expr_call.arguments.keywords.is_empty() => {
|
||||
let function = function_type
|
||||
.known(self.db)
|
||||
.and_then(KnownFunction::constraint_function)?;
|
||||
|
||||
let [ast::Expr::Name(ast::ExprName { id, .. }), class_info] =
|
||||
&*expr_call.arguments.args
|
||||
{
|
||||
let symbol = self.symbols().symbol_id_by_name(id).unwrap();
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let class_info_ty =
|
||||
inference.expression_ty(class_info.scoped_expression_id(self.db, scope));
|
||||
let symbol = self.symbols().symbol_id_by_name(id).unwrap();
|
||||
|
||||
let to_constraint = match function {
|
||||
KnownConstraintFunction::IsInstance => {
|
||||
|class_literal: ClassLiteralType<'db>| {
|
||||
Type::instance(class_literal.class)
|
||||
}
|
||||
let class_info_ty =
|
||||
inference.expression_ty(class_info.scoped_expression_id(self.db, scope));
|
||||
|
||||
let to_constraint = match function {
|
||||
KnownConstraintFunction::IsInstance => {
|
||||
|class_literal: ClassLiteralType<'db>| Type::instance(class_literal.class)
|
||||
}
|
||||
KnownConstraintFunction::IsSubclass => {
|
||||
|class_literal: ClassLiteralType<'db>| {
|
||||
Type::subclass_of(class_literal.class)
|
||||
}
|
||||
KnownConstraintFunction::IsSubclass => {
|
||||
|class_literal: ClassLiteralType<'db>| {
|
||||
Type::subclass_of(class_literal.class)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
generate_classinfo_constraint(self.db, &class_info_ty, to_constraint).map(
|
||||
|constraint| {
|
||||
let mut constraints = NarrowingConstraints::default();
|
||||
constraints.insert(symbol, constraint.negate_if(self.db, !is_positive));
|
||||
constraints
|
||||
},
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
generate_classinfo_constraint(self.db, &class_info_ty, to_constraint).map(
|
||||
|constraint| {
|
||||
let mut constraints = NarrowingConstraints::default();
|
||||
constraints.insert(symbol, constraint.negate_if(self.db, !is_positive));
|
||||
constraints
|
||||
},
|
||||
)
|
||||
}
|
||||
// for the expression `bool(E)`, we further narrow the type based on `E`
|
||||
Type::ClassLiteral(class_type)
|
||||
if expr_call.arguments.args.len() == 1
|
||||
&& expr_call.arguments.keywords.is_empty()
|
||||
&& class_type.class.is_known(self.db, KnownClass::Bool) =>
|
||||
{
|
||||
self.evaluate_expression_node_constraint(
|
||||
&expr_call.arguments.args[0],
|
||||
expression,
|
||||
is_positive,
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
||||
243
crates/red_knot_python_semantic/src/types/property_tests.rs
Normal file
243
crates/red_knot_python_semantic/src/types/property_tests.rs
Normal file
@@ -0,0 +1,243 @@
|
||||
//! This module contains quickcheck-based property tests for `Type`s.
|
||||
//!
|
||||
//! These tests are disabled by default, as they are non-deterministic and slow. You can
|
||||
//! run them explicitly using:
|
||||
//!
|
||||
//! ```sh
|
||||
//! cargo test -p red_knot_python_semantic -- --ignored types::property_tests::stable
|
||||
//! ```
|
||||
//!
|
||||
//! The number of tests (default: 100) can be controlled by setting the `QUICKCHECK_TESTS`
|
||||
//! environment variable. For example:
|
||||
//!
|
||||
//! ```sh
|
||||
//! QUICKCHECK_TESTS=10000 cargo test …
|
||||
//! ```
|
||||
//!
|
||||
//! If you want to run these tests for a longer period of time, it's advisable to run them
|
||||
//! in release mode. As some tests are slower than others, it's advisable to run them in a
|
||||
//! loop until they fail:
|
||||
//!
|
||||
//! ```sh
|
||||
//! export QUICKCHECK_TESTS=100000
|
||||
//! while cargo test --release -p red_knot_python_semantic -- \
|
||||
//! --ignored types::property_tests::stable; do :; done
|
||||
//! ```
|
||||
|
||||
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
|
||||
|
||||
use super::tests::Ty;
|
||||
use crate::db::tests::{setup_db, TestDb};
|
||||
use crate::types::KnownClass;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
|
||||
fn arbitrary_core_type(g: &mut Gen) -> Ty {
|
||||
// We could select a random integer here, but this would make it much less
|
||||
// likely to explore interesting edge cases:
|
||||
let int_lit = Ty::IntLiteral(*g.choose(&[-2, -1, 0, 1, 2]).unwrap());
|
||||
let bool_lit = Ty::BooleanLiteral(bool::arbitrary(g));
|
||||
g.choose(&[
|
||||
Ty::Never,
|
||||
Ty::Unknown,
|
||||
Ty::None,
|
||||
Ty::Any,
|
||||
int_lit,
|
||||
bool_lit,
|
||||
Ty::StringLiteral(""),
|
||||
Ty::StringLiteral("a"),
|
||||
Ty::LiteralString,
|
||||
Ty::BytesLiteral(""),
|
||||
Ty::BytesLiteral("\x00"),
|
||||
Ty::KnownClassInstance(KnownClass::Object),
|
||||
Ty::KnownClassInstance(KnownClass::Str),
|
||||
Ty::KnownClassInstance(KnownClass::Int),
|
||||
Ty::KnownClassInstance(KnownClass::Bool),
|
||||
Ty::KnownClassInstance(KnownClass::List),
|
||||
Ty::KnownClassInstance(KnownClass::Tuple),
|
||||
Ty::KnownClassInstance(KnownClass::FunctionType),
|
||||
Ty::KnownClassInstance(KnownClass::SpecialForm),
|
||||
Ty::KnownClassInstance(KnownClass::TypeVar),
|
||||
Ty::KnownClassInstance(KnownClass::TypeAliasType),
|
||||
Ty::KnownClassInstance(KnownClass::NoDefaultType),
|
||||
Ty::TypingLiteral,
|
||||
Ty::BuiltinClassLiteral("str"),
|
||||
Ty::BuiltinClassLiteral("int"),
|
||||
Ty::BuiltinClassLiteral("bool"),
|
||||
Ty::BuiltinClassLiteral("object"),
|
||||
])
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Constructs an arbitrary type.
|
||||
///
|
||||
/// The `size` parameter controls the depth of the type tree. For example,
|
||||
/// a simple type like `int` has a size of 0, `Union[int, str]` has a size
|
||||
/// of 1, `tuple[int, Union[str, bytes]]` has a size of 2, etc.
|
||||
fn arbitrary_type(g: &mut Gen, size: u32) -> Ty {
|
||||
if size == 0 {
|
||||
arbitrary_core_type(g)
|
||||
} else {
|
||||
match u32::arbitrary(g) % 4 {
|
||||
0 => arbitrary_core_type(g),
|
||||
1 => Ty::Union(
|
||||
(0..*g.choose(&[2, 3]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
),
|
||||
2 => Ty::Tuple(
|
||||
(0..*g.choose(&[0, 1, 2]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
),
|
||||
3 => Ty::Intersection {
|
||||
pos: (0..*g.choose(&[0, 1, 2]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
neg: (0..*g.choose(&[0, 1, 2]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Ty {
|
||||
fn arbitrary(g: &mut Gen) -> Ty {
|
||||
const MAX_SIZE: u32 = 2;
|
||||
arbitrary_type(g, MAX_SIZE)
|
||||
}
|
||||
|
||||
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
|
||||
// This is incredibly naive. We can do much better here by
|
||||
// trying various subsets of the elements in unions, tuples,
|
||||
// and intersections. For now, we only try to shrink by
|
||||
// reducing unions/tuples/intersections to a single element.
|
||||
match self.clone() {
|
||||
Ty::Union(types) => Box::new(types.into_iter()),
|
||||
Ty::Tuple(types) => Box::new(types.into_iter()),
|
||||
Ty::Intersection { pos, neg } => Box::new(pos.into_iter().chain(neg)),
|
||||
_ => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CACHED_DB: OnceLock<Arc<Mutex<TestDb>>> = OnceLock::new();
|
||||
|
||||
fn get_cached_db() -> MutexGuard<'static, TestDb> {
|
||||
let db = CACHED_DB.get_or_init(|| Arc::new(Mutex::new(setup_db())));
|
||||
db.lock().unwrap()
|
||||
}
|
||||
|
||||
/// A macro to define a property test for types.
|
||||
///
|
||||
/// The `$test_name` identifier specifies the name of the test function. The `$db` identifier
|
||||
/// is used to refer to the salsa database in the property to be tested. The actual property is
|
||||
/// specified using the syntax:
|
||||
///
|
||||
/// forall types t1, t2, ..., tn . <property>`
|
||||
///
|
||||
/// where `t1`, `t2`, ..., `tn` are identifiers that represent arbitrary types, and `<property>`
|
||||
/// is an expression using these identifiers.
|
||||
///
|
||||
macro_rules! type_property_test {
|
||||
($test_name:ident, $db:ident, forall types $($types:ident),+ . $property:expr) => {
|
||||
#[quickcheck_macros::quickcheck]
|
||||
#[ignore]
|
||||
fn $test_name($($types: crate::types::tests::Ty),+) -> bool {
|
||||
let db_cached = super::get_cached_db();
|
||||
let $db = &*db_cached;
|
||||
$(let $types = $types.into_type($db);)+
|
||||
|
||||
$property
|
||||
}
|
||||
};
|
||||
// A property test with a logical implication.
|
||||
($name:ident, $db:ident, forall types $($types:ident),+ . $premise:expr => $conclusion:expr) => {
|
||||
type_property_test!($name, $db, forall types $($types),+ . !($premise) || ($conclusion));
|
||||
};
|
||||
}
|
||||
|
||||
mod stable {
|
||||
// `T` is equivalent to itself.
|
||||
type_property_test!(
|
||||
equivalent_to_is_reflexive, db,
|
||||
forall types t. t.is_equivalent_to(db, t)
|
||||
);
|
||||
|
||||
// `T` is a subtype of itself.
|
||||
type_property_test!(
|
||||
subtype_of_is_reflexive, db,
|
||||
forall types t. t.is_subtype_of(db, t)
|
||||
);
|
||||
|
||||
// `S <: T` and `T <: U` implies that `S <: U`.
|
||||
type_property_test!(
|
||||
subtype_of_is_transitive, db,
|
||||
forall types s, t, u. s.is_subtype_of(db, t) && t.is_subtype_of(db, u) => s.is_subtype_of(db, u)
|
||||
);
|
||||
|
||||
// `T` is not disjoint from itself, unless `T` is `Never`.
|
||||
type_property_test!(
|
||||
disjoint_from_is_irreflexive, db,
|
||||
forall types t. t.is_disjoint_from(db, t) => t.is_never()
|
||||
);
|
||||
|
||||
// `S` is disjoint from `T` implies that `T` is disjoint from `S`.
|
||||
type_property_test!(
|
||||
disjoint_from_is_symmetric, db,
|
||||
forall types s, t. s.is_disjoint_from(db, t) == t.is_disjoint_from(db, s)
|
||||
);
|
||||
|
||||
// `S <: T` implies that `S` is not disjoint from `T`, unless `S` is `Never`.
|
||||
type_property_test!(
|
||||
subtype_of_implies_not_disjoint_from, db,
|
||||
forall types s, t. s.is_subtype_of(db, t) => !s.is_disjoint_from(db, t) || s.is_never()
|
||||
);
|
||||
|
||||
// `T` can be assigned to itself.
|
||||
type_property_test!(
|
||||
assignable_to_is_reflexive, db,
|
||||
forall types t. t.is_assignable_to(db, t)
|
||||
);
|
||||
|
||||
// `S <: T` implies that `S` can be assigned to `T`.
|
||||
type_property_test!(
|
||||
subtype_of_implies_assignable_to, db,
|
||||
forall types s, t. s.is_subtype_of(db, t) => s.is_assignable_to(db, t)
|
||||
);
|
||||
|
||||
// If `T` is a singleton, it is also single-valued.
|
||||
type_property_test!(
|
||||
singleton_implies_single_valued, db,
|
||||
forall types t. t.is_singleton(db) => t.is_single_valued(db)
|
||||
);
|
||||
|
||||
// If `T` contains a gradual form, it should not participate in subtyping
|
||||
type_property_test!(
|
||||
non_fully_static_types_do_not_participate_in_subtyping, db,
|
||||
forall types s, t. !s.is_fully_static(db) => !s.is_subtype_of(db, t) && !t.is_subtype_of(db, s)
|
||||
);
|
||||
}
|
||||
|
||||
/// This module contains property tests that currently lead to many false positives.
|
||||
///
|
||||
/// The reason for this is our insufficient understanding of equivalence of types. For
|
||||
/// example, we currently consider `int | str` and `str | int` to be different types.
|
||||
/// Similar issues exist for intersection types. Once this is resolved, we can move these
|
||||
/// tests to the `stable` section. In the meantime, it can still be useful to run these
|
||||
/// tests (using [`types::property_tests::flaky`]), to see if there are any new obvious bugs.
|
||||
mod flaky {
|
||||
// `S <: T` and `T <: S` implies that `S` is equivalent to `T`.
|
||||
type_property_test!(
|
||||
subtype_of_is_antisymmetric, db,
|
||||
forall types s, t. s.is_subtype_of(db, t) && t.is_subtype_of(db, s) => s.is_equivalent_to(db, t)
|
||||
);
|
||||
|
||||
// Negating `T` twice is equivalent to `T`.
|
||||
type_property_test!(
|
||||
double_negation_is_identity, db,
|
||||
forall types t. t.negate(db).negate(db).is_equivalent_to(db, t)
|
||||
);
|
||||
}
|
||||
@@ -189,32 +189,9 @@ impl<'db> Parameter<'db> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::db::tests::{setup_db, TestDb};
|
||||
use crate::types::{global_symbol, FunctionType};
|
||||
use crate::ProgramSettings;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
|
||||
pub(crate) fn setup_db() -> TestDb {
|
||||
let db = TestDb::new();
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&src_root)
|
||||
.unwrap();
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
&ProgramSettings {
|
||||
target_version: PythonVersion::default(),
|
||||
search_paths: SearchPathSettings::new(src_root),
|
||||
},
|
||||
)
|
||||
.expect("Valid search path settings");
|
||||
|
||||
db
|
||||
}
|
||||
use ruff_db::system::DbWithTestSystem;
|
||||
|
||||
#[track_caller]
|
||||
fn get_function_f<'db>(db: &'db TestDb, file: &'static str) -> FunctionType<'db> {
|
||||
|
||||
@@ -95,7 +95,8 @@ impl<'db> Unpacker<'db> {
|
||||
// there would be a cost and it's not clear that it's worth it.
|
||||
let value_ty = Type::tuple(
|
||||
self.db,
|
||||
&vec![Type::LiteralString; string_literal_ty.len(self.db)],
|
||||
std::iter::repeat(Type::LiteralString)
|
||||
.take(string_literal_ty.python_len(self.db)),
|
||||
);
|
||||
self.unpack(target, value_ty, scope);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
|
||||
use camino::Utf8Path;
|
||||
use dir_test::{dir_test, Fixture};
|
||||
use std::path::Path;
|
||||
|
||||
/// See `crates/red_knot_test/README.md` for documentation on these tests.
|
||||
#[dir_test(
|
||||
@@ -10,16 +9,46 @@ use dir_test::{dir_test, Fixture};
|
||||
)]
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn mdtest(fixture: Fixture<&str>) {
|
||||
let fixture_path = Path::new(fixture.path());
|
||||
let fixture_path = Utf8Path::new(fixture.path());
|
||||
let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let workspace_root = crate_dir.parent().and_then(Path::parent).unwrap();
|
||||
let workspace_root = crate_dir.ancestors().nth(2).unwrap();
|
||||
|
||||
let long_title = fixture_path
|
||||
.strip_prefix(workspace_root)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let short_title = fixture_path.file_name().and_then(OsStr::to_str).unwrap();
|
||||
let long_title = fixture_path.strip_prefix(workspace_root).unwrap();
|
||||
let short_title = fixture_path.file_name().unwrap();
|
||||
|
||||
red_knot_test::run(fixture_path, long_title, short_title);
|
||||
let test_name = test_name("mdtest", fixture_path);
|
||||
|
||||
red_knot_test::run(fixture_path, long_title.as_str(), short_title, &test_name);
|
||||
}
|
||||
|
||||
/// Constructs the test name used for individual markdown files
|
||||
///
|
||||
/// This code is copied from <https://github.com/fe-lang/dir-test/blob/1c0f41c480a3490bc2653a043ff6e3f8085a1f47/macros/src/lib.rs#L104-L138>
|
||||
/// and should be updated if they diverge
|
||||
fn test_name(test_func_name: &str, fixture_path: &Utf8Path) -> String {
|
||||
assert!(fixture_path.is_file());
|
||||
|
||||
let dir_path = format!("{}/resources/mdtest", std::env!("CARGO_MANIFEST_DIR"));
|
||||
let rel_path = fixture_path.strip_prefix(dir_path).unwrap();
|
||||
assert!(rel_path.is_relative());
|
||||
|
||||
let mut test_name = test_func_name.to_owned();
|
||||
test_name.push_str("__");
|
||||
|
||||
for component in rel_path.parent().unwrap().components() {
|
||||
let component = component
|
||||
.as_str()
|
||||
.replace(|c: char| c.is_ascii_punctuation(), "_");
|
||||
test_name.push_str(&component);
|
||||
test_name.push('_');
|
||||
}
|
||||
|
||||
test_name.push_str(
|
||||
&rel_path
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.replace(|c: char| c.is_ascii_punctuation(), "_"),
|
||||
);
|
||||
|
||||
test_name
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ pub(crate) struct Requester<'s> {
|
||||
response_handlers: FxHashMap<lsp_server::RequestId, ResponseBuilder<'s>>,
|
||||
}
|
||||
|
||||
impl<'s> Client<'s> {
|
||||
impl Client<'_> {
|
||||
pub(super) fn new(sender: ClientSender) -> Self {
|
||||
Self {
|
||||
notifier: Notifier(sender.clone()),
|
||||
|
||||
@@ -20,6 +20,7 @@ ruff_source_file = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
camino = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
|
||||
@@ -184,8 +184,11 @@ The tests are run independently, in independent in-memory file systems and with
|
||||
[Salsa](https://github.com/salsa-rs/salsa) databases. This means that each is a from-scratch run of
|
||||
the type checker, with no data persisting from any previous test.
|
||||
|
||||
Due to `cargo test` limitations, an entire test suite (Markdown file) is run as a single Rust test,
|
||||
so it's not possible to select individual tests within it to run.
|
||||
It is possible to filter to individual tests within a single markdown file using the
|
||||
`MDTEST_TEST_FILTER` environment variable. This variable will match any tests which contain the
|
||||
value as a case-sensitive substring in its name. An example test name is
|
||||
`unpacking.md - Unpacking - Tuple - Multiple assignment`, which contains the name of the markdown
|
||||
file and its parent headers joined together with hyphens.
|
||||
|
||||
## Structured test suites
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use camino::Utf8Path;
|
||||
use colored::Colorize;
|
||||
use parser as test_parser;
|
||||
use red_knot_python_semantic::types::check_types;
|
||||
@@ -7,7 +8,6 @@ use ruff_db::parsed::parsed_module;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
use ruff_source_file::LineIndex;
|
||||
use ruff_text_size::TextSize;
|
||||
use std::path::Path;
|
||||
|
||||
mod assertion;
|
||||
mod db;
|
||||
@@ -15,23 +15,30 @@ mod diagnostic;
|
||||
mod matcher;
|
||||
mod parser;
|
||||
|
||||
const MDTEST_TEST_FILTER: &str = "MDTEST_TEST_FILTER";
|
||||
|
||||
/// Run `path` as a markdown test suite with given `title`.
|
||||
///
|
||||
/// Panic on test failure, and print failure details.
|
||||
#[allow(clippy::print_stdout)]
|
||||
pub fn run(path: &Path, long_title: &str, short_title: &str) {
|
||||
pub fn run(path: &Utf8Path, long_title: &str, short_title: &str, test_name: &str) {
|
||||
let source = std::fs::read_to_string(path).unwrap();
|
||||
let suite = match test_parser::parse(short_title, &source) {
|
||||
Ok(suite) => suite,
|
||||
Err(err) => {
|
||||
panic!("Error parsing `{}`: {err}", path.to_str().unwrap())
|
||||
panic!("Error parsing `{path}`: {err}")
|
||||
}
|
||||
};
|
||||
|
||||
let mut db = db::Db::setup(SystemPathBuf::from("/src"));
|
||||
|
||||
let filter = std::env::var(MDTEST_TEST_FILTER).ok();
|
||||
let mut any_failures = false;
|
||||
for test in suite.tests() {
|
||||
if filter.as_ref().is_some_and(|f| !test.name().contains(f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove all files so that the db is in a "fresh" state.
|
||||
db.memory_file_system().remove_all();
|
||||
Files::sync_all(&mut db);
|
||||
@@ -54,6 +61,15 @@ pub fn run(path: &Path, long_title: &str, short_title: &str) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"\nTo rerun this specific test, set the environment variable: {MDTEST_TEST_FILTER}=\"{}\"",
|
||||
test.name()
|
||||
);
|
||||
println!(
|
||||
"{MDTEST_TEST_FILTER}=\"{}\" cargo test -p red_knot_python_semantic --test mdtest -- {test_name}",
|
||||
test.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
5052fa2f18db4493892e0f2775030683c9d06531
|
||||
0a2da01946a406ede42e9c66f416a7e7758991d6
|
||||
|
||||
@@ -33,6 +33,7 @@ _contextvars: 3.7-
|
||||
_csv: 3.0-
|
||||
_ctypes: 3.0-
|
||||
_curses: 3.0-
|
||||
_curses_panel: 3.0-
|
||||
_dbm: 3.0-
|
||||
_decimal: 3.3-
|
||||
_dummy_thread: 3.0-3.8
|
||||
@@ -40,6 +41,7 @@ _dummy_threading: 3.0-3.8
|
||||
_frozen_importlib: 3.0-
|
||||
_frozen_importlib_external: 3.5-
|
||||
_gdbm: 3.0-
|
||||
_hashlib: 3.0-
|
||||
_heapq: 3.0-
|
||||
_imp: 3.0-
|
||||
_interpchannels: 3.13-
|
||||
@@ -52,6 +54,7 @@ _lsprof: 3.0-
|
||||
_lzma: 3.3-
|
||||
_markupbase: 3.0-
|
||||
_msi: 3.0-3.12
|
||||
_multibytecodec: 3.0-
|
||||
_operator: 3.4-
|
||||
_osx_support: 3.0-
|
||||
_posixsubprocess: 3.2-
|
||||
@@ -139,6 +142,12 @@ doctest: 3.0-
|
||||
dummy_threading: 3.0-3.8
|
||||
email: 3.0-
|
||||
encodings: 3.0-
|
||||
encodings.cp1125: 3.4-
|
||||
encodings.cp273: 3.4-
|
||||
encodings.cp858: 3.2-
|
||||
encodings.koi8_t: 3.5-
|
||||
encodings.kz1048: 3.5-
|
||||
encodings.mac_centeuro: 3.0-3.8
|
||||
ensurepip: 3.0-
|
||||
enum: 3.4-
|
||||
errno: 3.0-
|
||||
|
||||
@@ -2,10 +2,13 @@ import codecs
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer
|
||||
from collections.abc import Callable
|
||||
from typing import Literal, overload
|
||||
from typing import Literal, final, overload, type_check_only
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# This type is not exposed; it is defined in unicodeobject.c
|
||||
# At runtime it calls itself builtins.EncodingMap
|
||||
@final
|
||||
@type_check_only
|
||||
class _EncodingMap:
|
||||
def size(self) -> int: ...
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers.
|
||||
@final
|
||||
class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __reversed__(self) -> Iterator[_KT_co]: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@@ -81,6 +82,7 @@ class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
|
||||
@final
|
||||
class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
def __reversed__(self) -> Iterator[_VT_co]: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@property
|
||||
def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ...
|
||||
@@ -88,6 +90,7 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
@final
|
||||
class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import csv
|
||||
import sys
|
||||
from _typeshed import SupportsWrite
|
||||
from collections.abc import Iterable, Iterator
|
||||
from typing import Any, Final
|
||||
from typing_extensions import TypeAlias
|
||||
from collections.abc import Iterable
|
||||
from typing import Any, Final, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
__version__: Final[str]
|
||||
|
||||
@@ -45,17 +45,47 @@ class Dialect:
|
||||
strict: bool = False,
|
||||
) -> None: ...
|
||||
|
||||
class _reader(Iterator[list[str]]):
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
line_num: int
|
||||
def __next__(self) -> list[str]: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
# This class calls itself _csv.reader.
|
||||
class Reader:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
line_num: int
|
||||
def __iter__(self) -> Self: ...
|
||||
def __next__(self) -> list[str]: ...
|
||||
|
||||
class _writer:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
def writerow(self, row: Iterable[Any]) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
|
||||
# This class calls itself _csv.writer.
|
||||
class Writer:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def writerow(self, row: Iterable[Any], /) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]], /) -> None: ...
|
||||
else:
|
||||
def writerow(self, row: Iterable[Any]) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
|
||||
|
||||
# For the return types below.
|
||||
# These aliases can be removed when typeshed drops support for 3.9.
|
||||
_reader = Reader
|
||||
_writer = Writer
|
||||
else:
|
||||
# This class is not exposed. It calls itself _csv.reader.
|
||||
@type_check_only
|
||||
class _reader:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
line_num: int
|
||||
def __iter__(self) -> Self: ...
|
||||
def __next__(self) -> list[str]: ...
|
||||
|
||||
# This class is not exposed. It calls itself _csv.writer.
|
||||
@type_check_only
|
||||
class _writer:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
def writerow(self, row: Iterable[Any]) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
|
||||
|
||||
def writer(
|
||||
csvfile: SupportsWrite[str],
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import _typeshed
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer, WriteableBuffer
|
||||
from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
|
||||
from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p
|
||||
from typing import Any, ClassVar, Generic, TypeVar, overload
|
||||
from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
@@ -47,46 +48,79 @@ if sys.platform == "win32":
|
||||
def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ...
|
||||
def FreeLibrary(handle: int, /) -> None: ...
|
||||
|
||||
class _CDataMeta(type):
|
||||
# By default mypy complains about the following two methods, because strictly speaking cls
|
||||
# might not be a Type[_CT]. However this can never actually happen, because the only class that
|
||||
# uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here.
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
else:
|
||||
def dlclose(handle: int, /) -> None: ...
|
||||
# The default for flag is RTLD_GLOBAL|RTLD_LOCAL, which is platform dependent.
|
||||
def dlopen(name: StrOrBytesPath, flag: int = ..., /) -> int: ...
|
||||
def dlsym(handle: int, name: str, /) -> int: ...
|
||||
|
||||
class _CData(metaclass=_CDataMeta):
|
||||
if sys.version_info >= (3, 13):
|
||||
# This class is not exposed. It calls itself _ctypes.CType_Type.
|
||||
@type_check_only
|
||||
class _CType_Type(type):
|
||||
# By default mypy complains about the following two methods, because strictly speaking cls
|
||||
# might not be a Type[_CT]. However this doesn't happen because this is only a
|
||||
# metaclass for subclasses of _CData.
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
_CTypeBaseType = _CType_Type
|
||||
|
||||
else:
|
||||
_CTypeBaseType = type
|
||||
|
||||
# This class is not exposed.
|
||||
@type_check_only
|
||||
class _CData:
|
||||
_b_base_: int
|
||||
_b_needsfree_: bool
|
||||
_objects: Mapping[Any, int] | None
|
||||
# At runtime the following classmethods are available only on classes, not
|
||||
# on instances. This can't be reflected properly in the type system:
|
||||
#
|
||||
# Structure.from_buffer(...) # valid at runtime
|
||||
# Structure(...).from_buffer(...) # invalid at runtime
|
||||
#
|
||||
@classmethod
|
||||
def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_address(cls, address: int) -> Self: ...
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
@classmethod
|
||||
def in_dll(cls, library: CDLL, name: str) -> Self: ...
|
||||
def __buffer__(self, flags: int, /) -> memoryview: ...
|
||||
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
|
||||
def __ctypes_from_outparam__(self, /) -> Self: ...
|
||||
|
||||
class _SimpleCData(_CData, Generic[_T]):
|
||||
# this is a union of all the subclasses of _CData, which is useful because of
|
||||
# the methods that are present on each of those subclasses which are not present
|
||||
# on _CData itself.
|
||||
_CDataType: TypeAlias = _SimpleCData[Any] | _Pointer[Any] | CFuncPtr | Union | Structure | Array[Any]
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.PyCSimpleType.
|
||||
@type_check_only
|
||||
class _PyCSimpleType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class _SimpleCData(_CData, Generic[_T], metaclass=_PyCSimpleType):
|
||||
value: _T
|
||||
# The TypeVar can be unsolved here,
|
||||
# but we can't use overloads without creating many, many mypy false-positive errors
|
||||
def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse]
|
||||
def __ctypes_from_outparam__(self, /) -> _T: ... # type: ignore[override]
|
||||
|
||||
class _CanCastTo(_CData): ...
|
||||
class _PointerLike(_CanCastTo): ...
|
||||
|
||||
class _Pointer(_PointerLike, _CData, Generic[_CT]):
|
||||
# This type is not exposed. It calls itself _ctypes.PyCPointerType.
|
||||
@type_check_only
|
||||
class _PyCPointerType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
def set_type(self, type: Any, /) -> None: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class _Pointer(_PointerLike, _CData, Generic[_CT], metaclass=_PyCPointerType):
|
||||
_type_: type[_CT]
|
||||
contents: _CT
|
||||
@overload
|
||||
@@ -105,16 +139,32 @@ def POINTER(type: None, /) -> type[c_void_p]: ...
|
||||
def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ...
|
||||
def pointer(obj: _CT, /) -> _Pointer[_CT]: ...
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.CArgObject.
|
||||
@final
|
||||
@type_check_only
|
||||
class _CArgObject: ...
|
||||
|
||||
def byref(obj: _CData, offset: int = ...) -> _CArgObject: ...
|
||||
def byref(obj: _CData | _CDataType, offset: int = ...) -> _CArgObject: ...
|
||||
|
||||
_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData]
|
||||
_ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType]
|
||||
_PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any]
|
||||
|
||||
class CFuncPtr(_PointerLike, _CData):
|
||||
restype: type[_CData] | Callable[[int], Any] | None
|
||||
argtypes: Sequence[type[_CData]]
|
||||
# This class is not exposed. It calls itself _ctypes.PyCFuncPtrType.
|
||||
@type_check_only
|
||||
class _PyCFuncPtrType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType):
|
||||
restype: type[_CDataType] | Callable[[int], Any] | None
|
||||
argtypes: Sequence[type[_CDataType]]
|
||||
errcheck: _ECT
|
||||
# Abstract attribute that must be defined on subclasses
|
||||
_flags_: ClassVar[int]
|
||||
@@ -129,7 +179,7 @@ class CFuncPtr(_PointerLike, _CData):
|
||||
if sys.platform == "win32":
|
||||
@overload
|
||||
def __init__(
|
||||
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., /
|
||||
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., /
|
||||
) -> None: ...
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
@@ -137,30 +187,95 @@ class CFuncPtr(_PointerLike, _CData):
|
||||
_GetT = TypeVar("_GetT")
|
||||
_SetT = TypeVar("_SetT")
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.CField.
|
||||
@final
|
||||
@type_check_only
|
||||
class _CField(Generic[_CT, _GetT, _SetT]):
|
||||
offset: int
|
||||
size: int
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: type[Any] | None = None, /) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: Any, owner: type[Any] | None = None, /) -> _GetT: ...
|
||||
else:
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
|
||||
|
||||
def __set__(self, instance: Any, value: _SetT, /) -> None: ...
|
||||
|
||||
class _StructUnionMeta(_CDataMeta):
|
||||
_fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]]
|
||||
_pack_: int
|
||||
_anonymous_: Sequence[str]
|
||||
# This class is not exposed. It calls itself _ctypes.UnionType.
|
||||
@type_check_only
|
||||
class _UnionType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
# At runtime, various attributes are created on a Union subclass based
|
||||
# on its _fields_. This method doesn't exist, but represents those
|
||||
# dynamically created attributes.
|
||||
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class Union(_CData, metaclass=_UnionType):
|
||||
_fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]]
|
||||
_pack_: ClassVar[int]
|
||||
_anonymous_: ClassVar[Sequence[str]]
|
||||
if sys.version_info >= (3, 13):
|
||||
_align_: ClassVar[int]
|
||||
|
||||
class _StructUnionBase(_CData, metaclass=_StructUnionMeta):
|
||||
def __init__(self, *args: Any, **kw: Any) -> None: ...
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
|
||||
class Union(_StructUnionBase): ...
|
||||
class Structure(_StructUnionBase): ...
|
||||
# This class is not exposed. It calls itself _ctypes.PyCStructType.
|
||||
@type_check_only
|
||||
class _PyCStructType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
# At runtime, various attributes are created on a Structure subclass based
|
||||
# on its _fields_. This method doesn't exist, but represents those
|
||||
# dynamically created attributes.
|
||||
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class Array(_CData, Generic[_CT]):
|
||||
class Structure(_CData, metaclass=_PyCStructType):
|
||||
_fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]]
|
||||
_pack_: ClassVar[int]
|
||||
_anonymous_: ClassVar[Sequence[str]]
|
||||
if sys.version_info >= (3, 13):
|
||||
_align_: ClassVar[int]
|
||||
|
||||
def __init__(self, *args: Any, **kw: Any) -> None: ...
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.PyCArrayType.
|
||||
@type_check_only
|
||||
class _PyCArrayType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class Array(_CData, Generic[_CT], metaclass=_PyCArrayType):
|
||||
@property
|
||||
@abstractmethod
|
||||
def _length_(self) -> int: ...
|
||||
@@ -205,9 +320,15 @@ class Array(_CData, Generic[_CT]):
|
||||
if sys.version_info >= (3, 9):
|
||||
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
|
||||
|
||||
def addressof(obj: _CData, /) -> int: ...
|
||||
def alignment(obj_or_type: _CData | type[_CData], /) -> int: ...
|
||||
def addressof(obj: _CData | _CDataType, /) -> int: ...
|
||||
def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...
|
||||
def get_errno() -> int: ...
|
||||
def resize(obj: _CData, size: int, /) -> None: ...
|
||||
def resize(obj: _CData | _CDataType, size: int, /) -> None: ...
|
||||
def set_errno(value: int, /) -> int: ...
|
||||
def sizeof(obj_or_type: _CData | type[_CData], /) -> int: ...
|
||||
def sizeof(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...
|
||||
def PyObj_FromPtr(address: int, /) -> Any: ...
|
||||
def Py_DECREF(o: _T, /) -> _T: ...
|
||||
def Py_INCREF(o: _T, /) -> _T: ...
|
||||
def buffer_info(o: _CData | _CDataType | type[_CData | _CDataType], /) -> tuple[str, int, tuple[int, ...]]: ...
|
||||
def call_cdeclfunction(address: int, arguments: tuple[Any, ...], /) -> Any: ...
|
||||
def call_function(address: int, arguments: tuple[Any, ...], /) -> Any: ...
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from _typeshed import ReadOnlyBuffer, SupportsRead
|
||||
from typing import IO, Any, NamedTuple, final, overload
|
||||
from curses import _ncurses_version
|
||||
from typing import IO, Any, final, overload
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# NOTE: This module is ordinarily only available on Unix, but the windows-curses
|
||||
@@ -549,9 +550,4 @@ class window: # undocumented
|
||||
@overload
|
||||
def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ...
|
||||
|
||||
class _ncurses_version(NamedTuple):
|
||||
major: int
|
||||
minor: int
|
||||
patch: int
|
||||
|
||||
ncurses_version: _ncurses_version
|
||||
|
||||
27
crates/red_knot_vendored/vendor/typeshed/stdlib/_curses_panel.pyi
vendored
Normal file
27
crates/red_knot_vendored/vendor/typeshed/stdlib/_curses_panel.pyi
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
from _curses import window
|
||||
from typing import final
|
||||
|
||||
__version__: str
|
||||
version: str
|
||||
|
||||
class error(Exception): ...
|
||||
|
||||
@final
|
||||
class panel:
|
||||
def above(self) -> panel: ...
|
||||
def below(self) -> panel: ...
|
||||
def bottom(self) -> None: ...
|
||||
def hidden(self) -> bool: ...
|
||||
def hide(self) -> None: ...
|
||||
def move(self, y: int, x: int, /) -> None: ...
|
||||
def replace(self, win: window, /) -> None: ...
|
||||
def set_userptr(self, obj: object, /) -> None: ...
|
||||
def show(self) -> None: ...
|
||||
def top(self) -> None: ...
|
||||
def userptr(self) -> object: ...
|
||||
def window(self) -> window: ...
|
||||
|
||||
def bottom_panel() -> panel: ...
|
||||
def new_panel(win: window, /) -> panel: ...
|
||||
def top_panel() -> panel: ...
|
||||
def update_panels() -> panel: ...
|
||||
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
from _typeshed import ReadOnlyBuffer, StrOrBytesPath
|
||||
from types import TracebackType
|
||||
from typing import TypeVar, overload
|
||||
from typing import TypeVar, final, overload, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
if sys.platform != "win32":
|
||||
@@ -13,6 +13,8 @@ if sys.platform != "win32":
|
||||
library: str
|
||||
|
||||
# Actual typename dbm, not exposed by the implementation
|
||||
@final
|
||||
@type_check_only
|
||||
class _dbm:
|
||||
def close(self) -> None: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
@@ -22,18 +24,17 @@ if sys.platform != "win32":
|
||||
def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ...
|
||||
def __delitem__(self, key: _KeyType) -> None: ...
|
||||
def __len__(self) -> int: ...
|
||||
def __del__(self) -> None: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> None: ...
|
||||
@overload
|
||||
def get(self, k: _KeyType) -> bytes | None: ...
|
||||
def get(self, k: _KeyType, /) -> bytes | None: ...
|
||||
@overload
|
||||
def get(self, k: _KeyType, default: _T) -> bytes | _T: ...
|
||||
def get(self, k: _KeyType, default: _T, /) -> bytes | _T: ...
|
||||
def keys(self) -> list[bytes]: ...
|
||||
def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ...
|
||||
# Don't exist at runtime
|
||||
def setdefault(self, k: _KeyType, default: _ValueType = ..., /) -> bytes: ...
|
||||
# This isn't true, but the class can't be instantiated. See #13024
|
||||
__new__: None # type: ignore[assignment]
|
||||
__init__: None # type: ignore[assignment]
|
||||
|
||||
|
||||
@@ -17,20 +17,13 @@ from decimal import (
|
||||
Rounded as Rounded,
|
||||
Subnormal as Subnormal,
|
||||
Underflow as Underflow,
|
||||
_ContextManager,
|
||||
)
|
||||
from types import TracebackType
|
||||
from typing import Final
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
_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]
|
||||
|
||||
|
||||
80
crates/red_knot_vendored/vendor/typeshed/stdlib/_hashlib.pyi
vendored
Normal file
80
crates/red_knot_vendored/vendor/typeshed/stdlib/_hashlib.pyi
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer
|
||||
from collections.abc import Callable
|
||||
from types import ModuleType
|
||||
from typing import AnyStr, final, overload
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
_DigestMod: TypeAlias = str | Callable[[], HASH] | ModuleType | None
|
||||
|
||||
openssl_md_meth_names: frozenset[str]
|
||||
|
||||
class HASH:
|
||||
@property
|
||||
def digest_size(self) -> int: ...
|
||||
@property
|
||||
def block_size(self) -> int: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
def copy(self) -> Self: ...
|
||||
def digest(self) -> bytes: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, obj: ReadableBuffer, /) -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
class UnsupportedDigestmodError(ValueError): ...
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
class HASHXOF(HASH):
|
||||
def digest(self, length: int) -> bytes: ... # type: ignore[override]
|
||||
def hexdigest(self, length: int) -> str: ... # type: ignore[override]
|
||||
|
||||
@final
|
||||
class HMAC:
|
||||
@property
|
||||
def digest_size(self) -> int: ...
|
||||
@property
|
||||
def block_size(self) -> int: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
def copy(self) -> Self: ...
|
||||
def digest(self) -> bytes: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, msg: ReadableBuffer) -> None: ...
|
||||
|
||||
@overload
|
||||
def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ...
|
||||
@overload
|
||||
def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...
|
||||
def get_fips_mode() -> int: ...
|
||||
def hmac_new(key: bytes | bytearray, msg: ReadableBuffer = b"", digestmod: _DigestMod = None) -> HMAC: ...
|
||||
def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
|
||||
def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
|
||||
|
||||
else:
|
||||
def new(name: str, string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_md5(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha1(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha224(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha256(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha384(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha512(string: ReadableBuffer = b"") -> HASH: ...
|
||||
|
||||
def hmac_digest(key: bytes | bytearray, msg: ReadableBuffer, digest: str) -> bytes: ...
|
||||
def pbkdf2_hmac(
|
||||
hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None
|
||||
) -> bytes: ...
|
||||
def scrypt(
|
||||
password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64
|
||||
) -> bytes: ...
|
||||
@@ -45,5 +45,6 @@ class make_scanner:
|
||||
def __init__(self, context: make_scanner) -> None: ...
|
||||
def __call__(self, string: str, index: int) -> tuple[Any, int]: ...
|
||||
|
||||
def encode_basestring(s: str, /) -> str: ...
|
||||
def encode_basestring_ascii(s: str, /) -> str: ...
|
||||
def scanstring(string: str, end: int, strict: bool = ...) -> tuple[str, int]: ...
|
||||
|
||||
44
crates/red_knot_vendored/vendor/typeshed/stdlib/_multibytecodec.pyi
vendored
Normal file
44
crates/red_knot_vendored/vendor/typeshed/stdlib/_multibytecodec.pyi
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
from _typeshed import ReadableBuffer
|
||||
from codecs import _ReadableStream, _WritableStream
|
||||
from collections.abc import Iterable
|
||||
from typing import final, type_check_only
|
||||
|
||||
# This class is not exposed. It calls itself _multibytecodec.MultibyteCodec.
|
||||
@final
|
||||
@type_check_only
|
||||
class _MultibyteCodec:
|
||||
def decode(self, input: ReadableBuffer, errors: str | None = None) -> str: ...
|
||||
def encode(self, input: str, errors: str | None = None) -> bytes: ...
|
||||
|
||||
class MultibyteIncrementalDecoder:
|
||||
errors: str
|
||||
def __init__(self, errors: str = "strict") -> None: ...
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
def getstate(self) -> tuple[bytes, int]: ...
|
||||
def reset(self) -> None: ...
|
||||
def setstate(self, state: tuple[bytes, int], /) -> None: ...
|
||||
|
||||
class MultibyteIncrementalEncoder:
|
||||
errors: str
|
||||
def __init__(self, errors: str = "strict") -> None: ...
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
def getstate(self) -> int: ...
|
||||
def reset(self) -> None: ...
|
||||
def setstate(self, state: int, /) -> None: ...
|
||||
|
||||
class MultibyteStreamReader:
|
||||
errors: str
|
||||
stream: _ReadableStream
|
||||
def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ...
|
||||
def read(self, sizeobj: int | None = None, /) -> str: ...
|
||||
def readline(self, sizeobj: int | None = None, /) -> str: ...
|
||||
def readlines(self, sizehintobj: int | None = None, /) -> list[str]: ...
|
||||
def reset(self) -> None: ...
|
||||
|
||||
class MultibyteStreamWriter:
|
||||
errors: str
|
||||
stream: _WritableStream
|
||||
def __init__(self, stream: _WritableStream, errors: str = "strict") -> None: ...
|
||||
def reset(self) -> None: ...
|
||||
def write(self, strobj: str, /) -> None: ...
|
||||
def writelines(self, lines: Iterable[str], /) -> None: ...
|
||||
@@ -4,7 +4,6 @@ from collections.abc import Callable, Sequence
|
||||
from typing import SupportsIndex
|
||||
|
||||
if sys.platform != "win32":
|
||||
def cloexec_pipe() -> tuple[int, int]: ...
|
||||
def fork_exec(
|
||||
args: Sequence[StrOrBytesPath] | None,
|
||||
executable_list: Sequence[bytes],
|
||||
|
||||
@@ -3,7 +3,7 @@ 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
|
||||
from typing_extensions import CapsuleType, TypeAlias
|
||||
|
||||
_CMSG: TypeAlias = tuple[int, int, bytes]
|
||||
_CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer]
|
||||
@@ -72,7 +72,8 @@ SO_SNDBUF: int
|
||||
SO_SNDLOWAT: int
|
||||
SO_SNDTIMEO: int
|
||||
SO_TYPE: int
|
||||
SO_USELOOPBACK: int
|
||||
if sys.platform != "linux":
|
||||
SO_USELOOPBACK: int
|
||||
if sys.platform == "win32":
|
||||
SO_EXCLUSIVEADDRUSE: int
|
||||
if sys.platform != "win32":
|
||||
@@ -87,7 +88,10 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SO_PEERSEC: int
|
||||
SO_PRIORITY: int
|
||||
SO_PROTOCOL: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
SO_SETFIB: int
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 13):
|
||||
SO_BINDTOIFINDEX: int
|
||||
|
||||
SOMAXCONN: int
|
||||
|
||||
@@ -99,27 +103,32 @@ MSG_TRUNC: int
|
||||
MSG_WAITALL: int
|
||||
if sys.platform != "win32":
|
||||
MSG_DONTWAIT: int
|
||||
MSG_EOF: int
|
||||
MSG_EOR: int
|
||||
MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not
|
||||
if sys.platform != "darwin":
|
||||
MSG_BCAST: int
|
||||
MSG_ERRQUEUE: int
|
||||
if sys.platform == "win32":
|
||||
MSG_BCAST: int
|
||||
MSG_MCAST: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
MSG_BTAG: int
|
||||
MSG_CMSG_CLOEXEC: int
|
||||
MSG_CONFIRM: int
|
||||
MSG_ETAG: int
|
||||
MSG_FASTOPEN: int
|
||||
MSG_MORE: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
MSG_EOF: int
|
||||
if sys.platform != "win32" and sys.platform != "linux" and sys.platform != "darwin":
|
||||
MSG_NOTIFICATION: int
|
||||
MSG_BTAG: int # Not FreeBSD either
|
||||
MSG_ETAG: int # Not FreeBSD either
|
||||
|
||||
SOL_IP: int
|
||||
SOL_SOCKET: int
|
||||
SOL_TCP: int
|
||||
SOL_UDP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
# Defined in socket.h for Linux, but these aren't always present for
|
||||
# some reason.
|
||||
SOL_ATALK: int
|
||||
SOL_AX25: int
|
||||
SOL_HCI: int
|
||||
@@ -128,10 +137,11 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SOL_ROSE: int
|
||||
|
||||
if sys.platform != "win32":
|
||||
SCM_CREDS: int
|
||||
SCM_RIGHTS: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SCM_CREDENTIALS: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
SCM_CREDS: int
|
||||
|
||||
IPPROTO_ICMP: int
|
||||
IPPROTO_IP: int
|
||||
@@ -143,21 +153,22 @@ IPPROTO_DSTOPTS: int
|
||||
IPPROTO_EGP: int
|
||||
IPPROTO_ESP: int
|
||||
IPPROTO_FRAGMENT: int
|
||||
IPPROTO_GGP: int
|
||||
IPPROTO_HOPOPTS: int
|
||||
IPPROTO_ICMPV6: int
|
||||
IPPROTO_IDP: int
|
||||
IPPROTO_IGMP: int
|
||||
IPPROTO_IPV4: int
|
||||
IPPROTO_IPV6: int
|
||||
IPPROTO_MAX: int
|
||||
IPPROTO_ND: int
|
||||
IPPROTO_NONE: int
|
||||
IPPROTO_PIM: int
|
||||
IPPROTO_PUP: int
|
||||
IPPROTO_ROUTING: int
|
||||
IPPROTO_SCTP: int
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "linux":
|
||||
IPPROTO_GGP: int
|
||||
IPPROTO_IPV4: int
|
||||
IPPROTO_MAX: int
|
||||
IPPROTO_ND: int
|
||||
if sys.platform == "win32":
|
||||
IPPROTO_CBT: int
|
||||
IPPROTO_ICLFXBM: int
|
||||
IPPROTO_IGP: int
|
||||
@@ -166,18 +177,19 @@ if sys.platform != "darwin":
|
||||
IPPROTO_RDP: int
|
||||
IPPROTO_ST: int
|
||||
if sys.platform != "win32":
|
||||
IPPROTO_EON: int
|
||||
IPPROTO_GRE: int
|
||||
IPPROTO_HELLO: int
|
||||
IPPROTO_IPCOMP: int
|
||||
IPPROTO_IPIP: int
|
||||
IPPROTO_RSVP: int
|
||||
IPPROTO_TP: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
IPPROTO_EON: int
|
||||
IPPROTO_HELLO: int
|
||||
IPPROTO_IPCOMP: int
|
||||
IPPROTO_XTP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IPPROTO_BIP: int
|
||||
IPPROTO_MOBILE: int
|
||||
IPPROTO_VRRP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
IPPROTO_BIP: int # Not FreeBSD either
|
||||
IPPROTO_MOBILE: int # Not FreeBSD either
|
||||
IPPROTO_VRRP: int # Not FreeBSD either
|
||||
if sys.version_info >= (3, 9) and sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.20, FreeBSD >= 10.1
|
||||
IPPROTO_UDPLITE: int
|
||||
@@ -202,11 +214,10 @@ IP_MULTICAST_IF: int
|
||||
IP_MULTICAST_LOOP: int
|
||||
IP_MULTICAST_TTL: int
|
||||
IP_OPTIONS: int
|
||||
IP_RECVDSTADDR: int
|
||||
if sys.platform != "linux":
|
||||
IP_RECVDSTADDR: int
|
||||
if sys.version_info >= (3, 10):
|
||||
IP_RECVTOS: int
|
||||
elif sys.platform != "win32" and sys.platform != "darwin":
|
||||
IP_RECVTOS: int
|
||||
IP_TOS: int
|
||||
IP_TTL: int
|
||||
if sys.platform != "win32":
|
||||
@@ -218,6 +229,7 @@ if sys.platform != "win32":
|
||||
IP_RETOPTS: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IP_TRANSPARENT: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11):
|
||||
IP_BIND_ADDRESS_NO_PORT: int
|
||||
if sys.version_info >= (3, 12):
|
||||
IP_ADD_SOURCE_MEMBERSHIP: int
|
||||
@@ -255,6 +267,9 @@ if sys.platform != "win32":
|
||||
IPV6_RECVPATHMTU: int
|
||||
IPV6_RECVPKTINFO: int
|
||||
IPV6_RTHDRDSTOPTS: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "darwin":
|
||||
IPV6_USE_MIN_MTU: int
|
||||
|
||||
EAI_AGAIN: int
|
||||
@@ -268,11 +283,12 @@ EAI_SERVICE: int
|
||||
EAI_SOCKTYPE: int
|
||||
if sys.platform != "win32":
|
||||
EAI_ADDRFAMILY: int
|
||||
EAI_OVERFLOW: int
|
||||
EAI_SYSTEM: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
EAI_BADHINTS: int
|
||||
EAI_MAX: int
|
||||
EAI_OVERFLOW: int
|
||||
EAI_PROTOCOL: int
|
||||
EAI_SYSTEM: int
|
||||
|
||||
AI_ADDRCONFIG: int
|
||||
AI_ALL: int
|
||||
@@ -281,7 +297,7 @@ AI_NUMERICHOST: int
|
||||
AI_NUMERICSERV: int
|
||||
AI_PASSIVE: int
|
||||
AI_V4MAPPED: int
|
||||
if sys.platform != "win32":
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
AI_DEFAULT: int
|
||||
AI_MASK: int
|
||||
AI_V4MAPPED_CFG: int
|
||||
@@ -293,6 +309,8 @@ NI_NAMEREQD: int
|
||||
NI_NOFQDN: int
|
||||
NI_NUMERICHOST: int
|
||||
NI_NUMERICSERV: int
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 13):
|
||||
NI_IDN: int
|
||||
|
||||
TCP_FASTOPEN: int
|
||||
TCP_KEEPCNT: int
|
||||
@@ -318,6 +336,27 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
TCP_SYNCNT: int
|
||||
TCP_USER_TIMEOUT: int
|
||||
TCP_WINDOW_CLAMP: int
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 12):
|
||||
TCP_CC_INFO: int
|
||||
TCP_FASTOPEN_CONNECT: int
|
||||
TCP_FASTOPEN_KEY: int
|
||||
TCP_FASTOPEN_NO_COOKIE: int
|
||||
TCP_INQ: int
|
||||
TCP_MD5SIG: int
|
||||
TCP_MD5SIG_EXT: int
|
||||
TCP_QUEUE_SEQ: int
|
||||
TCP_REPAIR: int
|
||||
TCP_REPAIR_OPTIONS: int
|
||||
TCP_REPAIR_QUEUE: int
|
||||
TCP_REPAIR_WINDOW: int
|
||||
TCP_SAVED_SYN: int
|
||||
TCP_SAVE_SYN: int
|
||||
TCP_THIN_DUPACK: int
|
||||
TCP_THIN_LINEAR_TIMEOUTS: int
|
||||
TCP_TIMESTAMP: int
|
||||
TCP_TX_DELAY: int
|
||||
TCP_ULP: int
|
||||
TCP_ZEROCOPY_RECEIVE: int
|
||||
|
||||
# --------------------
|
||||
# Specifically documented constants
|
||||
@@ -334,12 +373,13 @@ if sys.platform == "linux":
|
||||
CAN_ERR_FLAG: int
|
||||
CAN_ERR_MASK: int
|
||||
CAN_RAW: int
|
||||
CAN_RAW_ERR_FILTER: int
|
||||
CAN_RAW_FILTER: int
|
||||
CAN_RAW_LOOPBACK: int
|
||||
CAN_RAW_RECV_OWN_MSGS: int
|
||||
CAN_RTR_FLAG: int
|
||||
CAN_SFF_MASK: int
|
||||
if sys.version_info < (3, 11):
|
||||
CAN_RAW_ERR_FILTER: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.25
|
||||
@@ -437,12 +477,13 @@ if sys.platform == "linux":
|
||||
AF_RDS: int
|
||||
PF_RDS: int
|
||||
SOL_RDS: int
|
||||
# These are present in include/linux/rds.h but don't always show up
|
||||
# here.
|
||||
RDS_CANCEL_SENT_TO: int
|
||||
RDS_CMSG_RDMA_ARGS: int
|
||||
RDS_CMSG_RDMA_DEST: int
|
||||
RDS_CMSG_RDMA_MAP: int
|
||||
RDS_CMSG_RDMA_STATUS: int
|
||||
RDS_CMSG_RDMA_UPDATE: int
|
||||
RDS_CONG_MONITOR: int
|
||||
RDS_FREE_MR: int
|
||||
RDS_GET_MR: int
|
||||
@@ -456,6 +497,10 @@ if sys.platform == "linux":
|
||||
RDS_RDMA_USE_ONCE: int
|
||||
RDS_RECVERR: int
|
||||
|
||||
# This is supported by CPython but doesn't seem to be a real thing.
|
||||
# The closest existing constant in rds.h is RDS_CMSG_CONG_UPDATE
|
||||
# RDS_CMSG_RDMA_UPDATE: int
|
||||
|
||||
if sys.platform == "win32":
|
||||
SIO_RCVALL: int
|
||||
SIO_KEEPALIVE_VALS: int
|
||||
@@ -522,16 +567,17 @@ if sys.platform == "linux":
|
||||
if sys.platform != "win32" or sys.version_info >= (3, 9):
|
||||
# Documented as only available on BSD, macOS, but empirically sometimes
|
||||
# available on Windows
|
||||
AF_LINK: int
|
||||
if sys.platform != "linux":
|
||||
AF_LINK: int
|
||||
|
||||
has_ipv6: bool
|
||||
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "darwin" and sys.platform != "linux":
|
||||
if sys.platform != "win32" or sys.version_info >= (3, 9):
|
||||
BDADDR_ANY: str
|
||||
BDADDR_LOCAL: str
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
HCI_FILTER: int # not in NetBSD or DragonFlyBSD
|
||||
HCI_TIME_STAMP: int # not in FreeBSD, NetBSD, or DragonFlyBSD
|
||||
HCI_DATA_DIR: int # not in FreeBSD, NetBSD, or DragonFlyBSD
|
||||
@@ -580,36 +626,37 @@ if sys.version_info >= (3, 12):
|
||||
if sys.platform == "linux":
|
||||
# Netlink is defined by Linux
|
||||
AF_NETLINK: int
|
||||
NETLINK_ARPD: int
|
||||
NETLINK_CRYPTO: int
|
||||
NETLINK_DNRTMSG: int
|
||||
NETLINK_FIREWALL: int
|
||||
NETLINK_IP6_FW: int
|
||||
NETLINK_NFLOG: int
|
||||
NETLINK_ROUTE6: int
|
||||
NETLINK_ROUTE: int
|
||||
NETLINK_SKIP: int
|
||||
NETLINK_TAPBASE: int
|
||||
NETLINK_TCPDIAG: int
|
||||
NETLINK_USERSOCK: int
|
||||
NETLINK_W1: int
|
||||
NETLINK_XFRM: int
|
||||
# Technically still supported by CPython
|
||||
# NETLINK_ARPD: int # linux 2.0 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_ROUTE6: int # linux 2.2 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_SKIP: int # linux 2.0 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_TAPBASE: int # linux 2.2 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_TCPDIAG: int # linux 2.6.0 to 2.6.13 (EOL December 2005)
|
||||
# NETLINK_W1: int # linux 2.6.13 to 2.6.17 (EOL October 2006)
|
||||
|
||||
if sys.platform == "darwin":
|
||||
PF_SYSTEM: int
|
||||
SYSPROTO_CONTROL: int
|
||||
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "darwin" and sys.platform != "linux":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "win32":
|
||||
AF_BLUETOOTH: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
# Linux and some BSD support is explicit in the docs
|
||||
# Windows and macOS do not support in practice
|
||||
BTPROTO_HCI: int
|
||||
BTPROTO_L2CAP: int
|
||||
BTPROTO_SCO: int # not in FreeBSD
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "darwin" and sys.platform != "linux":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "win32":
|
||||
BTPROTO_RFCOMM: int
|
||||
|
||||
@@ -636,13 +683,14 @@ AF_SNA: int
|
||||
|
||||
if sys.platform != "win32":
|
||||
AF_ROUTE: int
|
||||
|
||||
if sys.platform == "darwin":
|
||||
AF_SYSTEM: int
|
||||
|
||||
if sys.platform != "darwin":
|
||||
AF_IRDA: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
AF_AAL5: int
|
||||
AF_ASH: int
|
||||
AF_ATMPVC: int
|
||||
AF_ATMSVC: int
|
||||
@@ -661,10 +709,12 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
|
||||
# Miscellaneous undocumented
|
||||
|
||||
if sys.platform != "win32":
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
LOCAL_PEERCRED: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
# Defined in linux socket.h, but this isn't always present for
|
||||
# some reason.
|
||||
IPX_TYPE: int
|
||||
|
||||
# ===== Classes =====
|
||||
@@ -792,4 +842,4 @@ def if_nameindex() -> list[tuple[int, str]]: ...
|
||||
def if_nametoindex(oname: str, /) -> int: ...
|
||||
def if_indextoname(index: int, /) -> str: ...
|
||||
|
||||
CAPI: object
|
||||
CAPI: CapsuleType
|
||||
|
||||
@@ -44,6 +44,12 @@ def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpa
|
||||
@overload
|
||||
def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ...
|
||||
|
||||
# Obsolete synonym for start_new_thread()
|
||||
@overload
|
||||
def start_new(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ...
|
||||
@overload
|
||||
def start_new(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
def interrupt_main(signum: signal.Signals = ..., /) -> None: ...
|
||||
|
||||
@@ -51,7 +57,9 @@ else:
|
||||
def interrupt_main() -> None: ...
|
||||
|
||||
def exit() -> NoReturn: ...
|
||||
def exit_thread() -> NoReturn: ... # Obsolete synonym for exit()
|
||||
def allocate_lock() -> LockType: ...
|
||||
def allocate() -> LockType: ... # Obsolete synonym for allocate_lock()
|
||||
def get_ident() -> int: ...
|
||||
def stack_size(size: int = 0, /) -> int: ...
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from threading import RLock
|
||||
from typing import Any
|
||||
from typing_extensions import Self, TypeAlias
|
||||
from weakref import ReferenceType
|
||||
@@ -8,6 +9,9 @@ _LocalDict: TypeAlias = dict[Any, Any]
|
||||
class _localimpl:
|
||||
key: str
|
||||
dicts: dict[int, tuple[ReferenceType[Any], _LocalDict]]
|
||||
# Keep localargs in sync with the *args, **kwargs annotation on local.__new__
|
||||
localargs: tuple[list[Any], dict[str, Any]]
|
||||
locallock: RLock
|
||||
def get_dict(self) -> _LocalDict: ...
|
||||
def create_dict(self) -> _LocalDict: ...
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class object:
|
||||
@property
|
||||
def __class__(self) -> type[Self]: ...
|
||||
@__class__.setter
|
||||
def __class__(self, type: type[object], /) -> None: ...
|
||||
def __class__(self, type: type[Self], /) -> None: ...
|
||||
def __init__(self) -> None: ...
|
||||
def __new__(cls) -> Self: ...
|
||||
# N.B. `object.__setattr__` and `object.__delattr__` are heavily special-cased by type checkers.
|
||||
@@ -1963,14 +1963,33 @@ class StopAsyncIteration(Exception):
|
||||
|
||||
class SyntaxError(Exception):
|
||||
msg: str
|
||||
filename: str | None
|
||||
lineno: int | None
|
||||
offset: int | None
|
||||
text: str | None
|
||||
filename: str | None
|
||||
# Errors are displayed differently if this attribute exists on the exception.
|
||||
# The value is always None.
|
||||
print_file_and_line: None
|
||||
if sys.version_info >= (3, 10):
|
||||
end_lineno: int | None
|
||||
end_offset: int | None
|
||||
|
||||
@overload
|
||||
def __init__(self) -> None: ...
|
||||
@overload
|
||||
def __init__(self, msg: object, /) -> None: ...
|
||||
# Second argument is the tuple (filename, lineno, offset, text)
|
||||
@overload
|
||||
def __init__(self, msg: str, info: tuple[str | None, int | None, int | None, str | None], /) -> None: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
# end_lineno and end_offset must both be provided if one is.
|
||||
@overload
|
||||
def __init__(
|
||||
self, msg: str, info: tuple[str | None, int | None, int | None, str | None, int | None, int | None], /
|
||||
) -> None: ...
|
||||
# If you provide more than two arguments, it still creates the SyntaxError, but
|
||||
# the arguments from the info tuple are not parsed. This form is omitted.
|
||||
|
||||
class SystemError(Exception): ...
|
||||
class TypeError(Exception): ...
|
||||
class ValueError(Exception): ...
|
||||
|
||||
@@ -126,6 +126,7 @@ class BZ2File(BaseStream, IO[bytes]):
|
||||
def readline(self, size: SupportsIndex = -1) -> bytes: ... # type: ignore[override]
|
||||
def readinto(self, b: WriteableBuffer) -> int: ...
|
||||
def readlines(self, size: SupportsIndex = -1) -> list[bytes]: ...
|
||||
def peek(self, n: int = 0) -> bytes: ...
|
||||
def seek(self, offset: int, whence: int = 0) -> int: ...
|
||||
def write(self, data: ReadableBuffer) -> int: ...
|
||||
def writelines(self, seq: Iterable[ReadableBuffer]) -> None: ...
|
||||
|
||||
@@ -3,7 +3,7 @@ from _codecs import *
|
||||
from _typeshed import ReadableBuffer
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Generator, Iterable
|
||||
from typing import Any, BinaryIO, Final, Literal, Protocol, TextIO
|
||||
from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO
|
||||
from typing_extensions import Self
|
||||
|
||||
__all__ = [
|
||||
@@ -202,6 +202,7 @@ class StreamWriter(Codec):
|
||||
def write(self, object: str) -> None: ...
|
||||
def writelines(self, list: Iterable[str]) -> None: ...
|
||||
def reset(self) -> None: ...
|
||||
def seek(self, offset: int, whence: int = 0) -> None: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ...
|
||||
def __getattr__(self, name: str, getattr: Callable[[Any, str], Any] = ...) -> Any: ...
|
||||
@@ -209,11 +210,14 @@ class StreamWriter(Codec):
|
||||
class StreamReader(Codec):
|
||||
stream: _ReadableStream
|
||||
errors: str
|
||||
# This is set to str, but some subclasses set to bytes instead.
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ...
|
||||
def read(self, size: int = -1, chars: int = -1, firstline: bool = False) -> str: ...
|
||||
def readline(self, size: int | None = None, keepends: bool = True) -> str: ...
|
||||
def readlines(self, sizehint: int | None = None, keepends: bool = True) -> list[str]: ...
|
||||
def reset(self) -> None: ...
|
||||
def seek(self, offset: int, whence: int = 0) -> None: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ...
|
||||
def __iter__(self) -> Self: ...
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sys
|
||||
from _typeshed import StrOrBytesPath, SupportsWrite
|
||||
from _typeshed import MaybeNone, StrOrBytesPath, SupportsWrite
|
||||
from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence
|
||||
from re import Pattern
|
||||
from typing import Any, ClassVar, Final, Literal, TypeVar, overload
|
||||
@@ -263,11 +263,11 @@ class RawConfigParser(_Parser):
|
||||
) -> _T: ...
|
||||
# This is incompatible with MutableMapping so we ignore the type
|
||||
@overload # type: ignore[override]
|
||||
def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | Any: ...
|
||||
def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | MaybeNone: ...
|
||||
@overload
|
||||
def get(
|
||||
self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T
|
||||
) -> str | _T | Any: ...
|
||||
) -> str | _T | MaybeNone: ...
|
||||
@overload
|
||||
def items(self, *, raw: bool = False, vars: _Section | None = None) -> ItemsView[str, SectionProxy]: ...
|
||||
@overload
|
||||
@@ -277,6 +277,8 @@ class RawConfigParser(_Parser):
|
||||
def remove_option(self, section: str, option: str) -> bool: ...
|
||||
def remove_section(self, section: str) -> bool: ...
|
||||
def optionxform(self, optionstr: str) -> str: ...
|
||||
@property
|
||||
def converters(self) -> ConverterMapping: ...
|
||||
|
||||
class ConfigParser(RawConfigParser):
|
||||
# This is incompatible with MutableMapping so we ignore the type
|
||||
@@ -300,28 +302,34 @@ class SectionProxy(MutableMapping[str, str]):
|
||||
def parser(self) -> RawConfigParser: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
def get( # type: ignore[override]
|
||||
# This is incompatible with MutableMapping so we ignore the type
|
||||
@overload # type: ignore[override]
|
||||
def get(
|
||||
self, option: str, *, raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, **kwargs: Any
|
||||
) -> str | None: ...
|
||||
@overload
|
||||
def get(
|
||||
self,
|
||||
option: str,
|
||||
fallback: str | None = None,
|
||||
fallback: _T,
|
||||
*,
|
||||
raw: bool = False,
|
||||
vars: _Section | None = None,
|
||||
_impl: Any | None = None,
|
||||
**kwargs: Any,
|
||||
) -> str | Any: ... # can be None in RawConfigParser's sections
|
||||
) -> str | _T: ...
|
||||
# These are partially-applied version of the methods with the same names in
|
||||
# RawConfigParser; the stubs should be kept updated together
|
||||
@overload
|
||||
def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int: ...
|
||||
def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int | None: ...
|
||||
@overload
|
||||
def getint(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> int | _T: ...
|
||||
@overload
|
||||
def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float: ...
|
||||
def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float | None: ...
|
||||
@overload
|
||||
def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> float | _T: ...
|
||||
@overload
|
||||
def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool: ...
|
||||
def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool | None: ...
|
||||
@overload
|
||||
def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> bool | _T: ...
|
||||
# SectionProxy can have arbitrary attributes when custom converters are used
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import abc
|
||||
import sys
|
||||
from _typeshed import FileDescriptorOrPath, Unused
|
||||
from abc import abstractmethod
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Generator, Iterator
|
||||
from types import TracebackType
|
||||
from typing import IO, Any, Generic, Protocol, TypeVar, overload, runtime_checkable
|
||||
@@ -38,16 +38,22 @@ _P = ParamSpec("_P")
|
||||
_ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None]
|
||||
_CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc)
|
||||
|
||||
# mypy and pyright object to this being both ABC and Protocol.
|
||||
# At runtime it inherits from ABC and is not a Protocol, but it is on the
|
||||
# allowlist for use as a Protocol.
|
||||
@runtime_checkable
|
||||
class AbstractContextManager(Protocol[_T_co, _ExitT_co]):
|
||||
class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __enter__(self) -> _T_co: ...
|
||||
@abstractmethod
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, /
|
||||
) -> _ExitT_co: ...
|
||||
|
||||
# mypy and pyright object to this being both ABC and Protocol.
|
||||
# At runtime it inherits from ABC and is not a Protocol, but it is on the
|
||||
# allowlist for use as a Protocol.
|
||||
@runtime_checkable
|
||||
class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]):
|
||||
class AbstractAsyncContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
async def __aenter__(self) -> _T_co: ...
|
||||
@abstractmethod
|
||||
async def __aexit__(
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import sys
|
||||
from typing import Final
|
||||
from typing import Final, NamedTuple, type_check_only
|
||||
|
||||
if sys.platform != "win32":
|
||||
class _Method: ...
|
||||
@type_check_only
|
||||
class _MethodBase(NamedTuple):
|
||||
name: str
|
||||
ident: str | None
|
||||
salt_chars: int
|
||||
total_size: int
|
||||
|
||||
class _Method(_MethodBase): ...
|
||||
METHOD_CRYPT: Final[_Method]
|
||||
METHOD_MD5: Final[_Method]
|
||||
METHOD_SHA256: Final[_Method]
|
||||
|
||||
@@ -8,8 +8,6 @@ from _csv import (
|
||||
__version__ as __version__,
|
||||
_DialectLike,
|
||||
_QuotingType,
|
||||
_reader,
|
||||
_writer,
|
||||
field_size_limit as field_size_limit,
|
||||
get_dialect as get_dialect,
|
||||
list_dialects as list_dialects,
|
||||
@@ -21,6 +19,10 @@ from _csv import (
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
from _csv import QUOTE_NOTNULL as QUOTE_NOTNULL, QUOTE_STRINGS as QUOTE_STRINGS
|
||||
if sys.version_info >= (3, 10):
|
||||
from _csv import Reader, Writer
|
||||
else:
|
||||
from _csv import _reader as Reader, _writer as Writer
|
||||
|
||||
from _typeshed import SupportsWrite
|
||||
from collections.abc import Collection, Iterable, Mapping, Sequence
|
||||
@@ -77,7 +79,7 @@ class DictReader(Generic[_T]):
|
||||
fieldnames: Sequence[_T] | None
|
||||
restkey: _T | None
|
||||
restval: str | Any | None
|
||||
reader: _reader
|
||||
reader: Reader
|
||||
dialect: _DialectLike
|
||||
line_num: int
|
||||
@overload
|
||||
@@ -125,7 +127,7 @@ class DictWriter(Generic[_T]):
|
||||
fieldnames: Collection[_T]
|
||||
restval: Any | None
|
||||
extrasaction: Literal["raise", "ignore"]
|
||||
writer: _writer
|
||||
writer: Writer
|
||||
def __init__(
|
||||
self,
|
||||
f: SupportsWrite[str],
|
||||
|
||||
@@ -10,13 +10,11 @@ from _ctypes import (
|
||||
_CanCastTo as _CanCastTo,
|
||||
_CArgObject as _CArgObject,
|
||||
_CData as _CData,
|
||||
_CDataMeta as _CDataMeta,
|
||||
_CDataType as _CDataType,
|
||||
_CField as _CField,
|
||||
_Pointer as _Pointer,
|
||||
_PointerLike as _PointerLike,
|
||||
_SimpleCData as _SimpleCData,
|
||||
_StructUnionBase as _StructUnionBase,
|
||||
_StructUnionMeta as _StructUnionMeta,
|
||||
addressof as addressof,
|
||||
alignment as alignment,
|
||||
byref as byref,
|
||||
@@ -28,7 +26,7 @@ from _ctypes import (
|
||||
)
|
||||
from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure
|
||||
from typing import Any, ClassVar, Generic, TypeVar
|
||||
from typing_extensions import TypeAlias
|
||||
from typing_extensions import Self, TypeAlias, deprecated
|
||||
|
||||
if sys.platform == "win32":
|
||||
from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error
|
||||
@@ -41,6 +39,7 @@ if sys.version_info >= (3, 9):
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_DLLT = TypeVar("_DLLT", bound=CDLL)
|
||||
_CT = TypeVar("_CT", bound=_CData)
|
||||
|
||||
DEFAULT_MODE: int
|
||||
|
||||
@@ -48,7 +47,7 @@ class ArgumentError(Exception): ...
|
||||
|
||||
class CDLL:
|
||||
_func_flags_: ClassVar[int]
|
||||
_func_restype_: ClassVar[_CData]
|
||||
_func_restype_: ClassVar[_CDataType]
|
||||
_name: str
|
||||
_handle: int
|
||||
_FuncPtr: type[_FuncPointer]
|
||||
@@ -91,15 +90,21 @@ class _NamedFuncPointer(_FuncPointer):
|
||||
__name__: str
|
||||
|
||||
def CFUNCTYPE(
|
||||
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
|
||||
restype: type[_CData | _CDataType] | None,
|
||||
*argtypes: type[_CData | _CDataType],
|
||||
use_errno: bool = ...,
|
||||
use_last_error: bool = ...,
|
||||
) -> type[_FuncPointer]: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
def WINFUNCTYPE(
|
||||
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
|
||||
restype: type[_CData | _CDataType] | None,
|
||||
*argtypes: type[_CData | _CDataType],
|
||||
use_errno: bool = ...,
|
||||
use_last_error: bool = ...,
|
||||
) -> type[_FuncPointer]: ...
|
||||
|
||||
def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> type[_FuncPointer]: ...
|
||||
def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ...
|
||||
|
||||
# Any type that can be implicitly converted to c_void_p when passed as a C function argument.
|
||||
# (bytes is not included here, see below.)
|
||||
@@ -112,12 +117,17 @@ _CVoidConstPLike: TypeAlias = _CVoidPLike | bytes
|
||||
|
||||
_CastT = TypeVar("_CastT", bound=_CanCastTo)
|
||||
|
||||
def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...
|
||||
def cast(obj: _CData | _CDataType | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...
|
||||
def create_string_buffer(init: int | bytes, size: int | None = None) -> Array[c_char]: ...
|
||||
|
||||
c_buffer = create_string_buffer
|
||||
|
||||
def create_unicode_buffer(init: int | str, size: int | None = None) -> Array[c_wchar]: ...
|
||||
@deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
|
||||
def SetPointerType(
|
||||
pointer: type[_Pointer[Any]], cls: Any # noqa: F811 # Redefinition of unused `pointer` from line 22
|
||||
) -> None: ...
|
||||
def ARRAY(typ: _CT, len: int) -> Array[_CT]: ... # Soft Deprecated, no plans to remove
|
||||
|
||||
if sys.platform == "win32":
|
||||
def DllCanUnloadNow() -> int: ...
|
||||
@@ -126,12 +136,12 @@ if sys.platform == "win32":
|
||||
|
||||
def memmove(dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ...
|
||||
def memset(dst: _CVoidPLike, c: int, count: int) -> int: ...
|
||||
def string_at(address: _CVoidConstPLike, size: int = -1) -> bytes: ...
|
||||
def string_at(ptr: _CVoidConstPLike, size: int = -1) -> bytes: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
def WinError(code: int | None = None, descr: str | None = None) -> OSError: ...
|
||||
|
||||
def wstring_at(address: _CVoidConstPLike, size: int = -1) -> str: ...
|
||||
def wstring_at(ptr: _CVoidConstPLike, size: int = -1) -> str: ...
|
||||
|
||||
class c_byte(_SimpleCData[int]): ...
|
||||
|
||||
@@ -140,6 +150,8 @@ class c_char(_SimpleCData[bytes]):
|
||||
|
||||
class c_char_p(_PointerLike, _SimpleCData[bytes | None]):
|
||||
def __init__(self, value: int | bytes | None = ...) -> None: ...
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
class c_double(_SimpleCData[float]): ...
|
||||
class c_longdouble(_SimpleCData[float]): ... # can be an alias for c_double
|
||||
@@ -155,7 +167,13 @@ class c_uint(_SimpleCData[int]): ... # can be an alias for c_ulong
|
||||
class c_ulong(_SimpleCData[int]): ...
|
||||
class c_ulonglong(_SimpleCData[int]): ... # can be an alias for c_ulong
|
||||
class c_ushort(_SimpleCData[int]): ...
|
||||
class c_void_p(_PointerLike, _SimpleCData[int | None]): ...
|
||||
|
||||
class c_void_p(_PointerLike, _SimpleCData[int | None]):
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
c_voidp = c_void_p # backwards compatibility (to a bug)
|
||||
|
||||
class c_wchar(_SimpleCData[str]): ...
|
||||
|
||||
c_int8 = c_byte
|
||||
@@ -174,6 +192,8 @@ class c_uint64(_SimpleCData[int]): ...
|
||||
|
||||
class c_wchar_p(_PointerLike, _SimpleCData[str | None]):
|
||||
def __init__(self, value: int | str | None = ...) -> None: ...
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
class c_bool(_SimpleCData[bool]):
|
||||
def __init__(self, value: bool = ...) -> None: ...
|
||||
|
||||
1
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/__init__.pyi
vendored
Normal file
1
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/__init__.pyi
vendored
Normal file
@@ -0,0 +1 @@
|
||||
__version__: str
|
||||
8
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dyld.pyi
vendored
Normal file
8
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dyld.pyi
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from collections.abc import Mapping
|
||||
from ctypes.macholib.dylib import dylib_info as dylib_info
|
||||
from ctypes.macholib.framework import framework_info as framework_info
|
||||
|
||||
__all__ = ["dyld_find", "framework_find", "framework_info", "dylib_info"]
|
||||
|
||||
def dyld_find(name: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ...
|
||||
def framework_find(fn: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ...
|
||||
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dylib.pyi
vendored
Normal file
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dylib.pyi
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
from typing import TypedDict, type_check_only
|
||||
|
||||
__all__ = ["dylib_info"]
|
||||
|
||||
# Actual result is produced by re.match.groupdict()
|
||||
@type_check_only
|
||||
class _DylibInfo(TypedDict):
|
||||
location: str
|
||||
name: str
|
||||
shortname: str
|
||||
version: str | None
|
||||
suffix: str | None
|
||||
|
||||
def dylib_info(filename: str) -> _DylibInfo | None: ...
|
||||
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/framework.pyi
vendored
Normal file
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/framework.pyi
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
from typing import TypedDict, type_check_only
|
||||
|
||||
__all__ = ["framework_info"]
|
||||
|
||||
# Actual result is produced by re.match.groupdict()
|
||||
@type_check_only
|
||||
class _FrameworkInfo(TypedDict):
|
||||
location: str
|
||||
name: str
|
||||
shortname: str
|
||||
version: str | None
|
||||
suffix: str | None
|
||||
|
||||
def framework_info(filename: str) -> _FrameworkInfo | None: ...
|
||||
@@ -4,3 +4,5 @@ def find_library(name: str) -> str | None: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
def find_msvcrt() -> str | None: ...
|
||||
|
||||
def test() -> None: ...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from _ctypes import _CArgObject, _CField
|
||||
from ctypes import (
|
||||
Array,
|
||||
Structure,
|
||||
_CField,
|
||||
_Pointer,
|
||||
_SimpleCData,
|
||||
c_byte,
|
||||
@@ -21,8 +21,8 @@ from ctypes import (
|
||||
c_wchar,
|
||||
c_wchar_p,
|
||||
)
|
||||
from typing import TypeVar
|
||||
from typing_extensions import TypeAlias
|
||||
from typing import Any, TypeVar
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
BYTE = c_byte
|
||||
WORD = c_ushort
|
||||
@@ -241,10 +241,16 @@ LPBYTE = PBYTE
|
||||
PBOOLEAN = PBYTE
|
||||
|
||||
# LP_c_char
|
||||
class PCHAR(_Pointer[CHAR]): ...
|
||||
class PCHAR(_Pointer[CHAR]):
|
||||
# this is inherited from ctypes.c_char_p, kind of.
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
# LP_c_wchar
|
||||
class PWCHAR(_Pointer[WCHAR]): ...
|
||||
class PWCHAR(_Pointer[WCHAR]):
|
||||
# inherited from ctypes.c_wchar_p, kind of
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
# LP_c_void_p
|
||||
class PHANDLE(_Pointer[HANDLE]): ...
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import sys
|
||||
from _curses import *
|
||||
from _curses import window as window
|
||||
from _typeshed import structseq
|
||||
from collections.abc import Callable
|
||||
from typing import TypeVar
|
||||
from typing import Final, TypeVar, final, type_check_only
|
||||
from typing_extensions import Concatenate, ParamSpec
|
||||
|
||||
# NOTE: The _curses module is ordinarily only available on Unix, but the
|
||||
@@ -25,3 +27,19 @@ def wrapper(func: Callable[Concatenate[window, _P], _T], /, *arg: _P.args, **kwd
|
||||
# it was mapped to the name 'window' in 3.8.
|
||||
# Kept here as a legacy alias in case any third-party code is relying on it.
|
||||
_CursesWindow = window
|
||||
|
||||
# At runtime this class is unexposed and calls itself curses.ncurses_version.
|
||||
# That name would conflict with the actual curses.ncurses_version, which is
|
||||
# an instance of this class.
|
||||
@final
|
||||
@type_check_only
|
||||
class _ncurses_version(structseq[int], tuple[int, int, int]):
|
||||
if sys.version_info >= (3, 10):
|
||||
__match_args__: Final = ("major", "minor", "patch")
|
||||
|
||||
@property
|
||||
def major(self) -> int: ...
|
||||
@property
|
||||
def minor(self) -> int: ...
|
||||
@property
|
||||
def patch(self) -> int: ...
|
||||
|
||||
@@ -1,22 +1 @@
|
||||
from _curses import window
|
||||
|
||||
version: str
|
||||
|
||||
class _Curses_Panel: # type is <class '_curses_panel.curses panel'> (note the space in the class name)
|
||||
def above(self) -> _Curses_Panel: ...
|
||||
def below(self) -> _Curses_Panel: ...
|
||||
def bottom(self) -> None: ...
|
||||
def hidden(self) -> bool: ...
|
||||
def hide(self) -> None: ...
|
||||
def move(self, y: int, x: int) -> None: ...
|
||||
def replace(self, win: window) -> None: ...
|
||||
def set_userptr(self, obj: object) -> None: ...
|
||||
def show(self) -> None: ...
|
||||
def top(self) -> None: ...
|
||||
def userptr(self) -> object: ...
|
||||
def window(self) -> window: ...
|
||||
|
||||
def bottom_panel() -> _Curses_Panel: ...
|
||||
def new_panel(win: window, /) -> _Curses_Panel: ...
|
||||
def top_panel() -> _Curses_Panel: ...
|
||||
def update_panels() -> _Curses_Panel: ...
|
||||
from _curses_panel import *
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import sys
|
||||
from abc import abstractmethod
|
||||
from time import struct_time
|
||||
from typing import ClassVar, Final, NamedTuple, NoReturn, SupportsIndex, final, overload
|
||||
from typing_extensions import Self, TypeAlias, deprecated
|
||||
from typing import ClassVar, Final, NoReturn, SupportsIndex, final, overload, type_check_only
|
||||
from typing_extensions import CapsuleType, Self, TypeAlias, deprecated
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC")
|
||||
@@ -40,10 +40,17 @@ if sys.version_info >= (3, 11):
|
||||
UTC: timezone
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
class _IsoCalendarDate(NamedTuple):
|
||||
year: int
|
||||
week: int
|
||||
weekday: int
|
||||
# This class calls itself datetime.IsoCalendarDate. It's neither
|
||||
# NamedTuple nor structseq.
|
||||
@final
|
||||
@type_check_only
|
||||
class _IsoCalendarDate(tuple[int, int, int]):
|
||||
@property
|
||||
def year(self) -> int: ...
|
||||
@property
|
||||
def week(self) -> int: ...
|
||||
@property
|
||||
def weekday(self) -> int: ...
|
||||
|
||||
class date:
|
||||
min: ClassVar[date]
|
||||
@@ -325,3 +332,5 @@ class datetime(date):
|
||||
def __sub__(self, value: Self, /) -> timedelta: ...
|
||||
@overload
|
||||
def __sub__(self, value: timedelta, /) -> Self: ...
|
||||
|
||||
datetime_CAPI: CapsuleType
|
||||
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
from _typeshed import StrOrBytesPath
|
||||
from collections.abc import Iterator, MutableMapping
|
||||
from types import TracebackType
|
||||
from typing import Literal
|
||||
from typing import Literal, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
__all__ = ["open", "whichdb", "error"]
|
||||
@@ -89,6 +89,8 @@ class _Database(MutableMapping[_KeyType, bytes]):
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> None: ...
|
||||
|
||||
# This class is not exposed. It calls itself dbm.error.
|
||||
@type_check_only
|
||||
class _error(Exception): ...
|
||||
|
||||
error: tuple[type[_error], type[OSError]]
|
||||
|
||||
@@ -24,7 +24,8 @@ from _decimal import (
|
||||
setcontext as setcontext,
|
||||
)
|
||||
from collections.abc import Container, Sequence
|
||||
from typing import Any, ClassVar, Literal, NamedTuple, overload
|
||||
from types import TracebackType
|
||||
from typing import Any, ClassVar, Literal, NamedTuple, final, overload, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
_Decimal: TypeAlias = Decimal | int
|
||||
@@ -35,6 +36,14 @@ _TrapType: TypeAlias = type[DecimalException]
|
||||
# At runtime, these classes are implemented in C as part of "_decimal".
|
||||
# However, they consider themselves to live in "decimal", so we'll put them here.
|
||||
|
||||
# This type isn't exposed at runtime. It calls itself decimal.ContextManager
|
||||
@final
|
||||
@type_check_only
|
||||
class _ContextManager:
|
||||
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: ...
|
||||
|
||||
class DecimalTuple(NamedTuple):
|
||||
sign: int
|
||||
digits: tuple[int, ...]
|
||||
|
||||
@@ -165,7 +165,7 @@ class CCompiler:
|
||||
def execute(
|
||||
self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1
|
||||
) -> None: ...
|
||||
def spawn(self, cmd: list[str]) -> None: ...
|
||||
def spawn(self, cmd: Iterable[str]) -> None: ...
|
||||
def mkpath(self, name: str, mode: int = 0o777) -> None: ...
|
||||
@overload
|
||||
def move_file(self, src: StrPath, dst: _StrPathT) -> _StrPathT | str: ...
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
from collections.abc import Iterable
|
||||
from typing import Literal
|
||||
|
||||
def spawn(
|
||||
cmd: list[str], search_path: bool | Literal[0, 1] = 1, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0
|
||||
cmd: Iterable[str],
|
||||
search_path: bool | Literal[0, 1] = 1,
|
||||
verbose: bool | Literal[0, 1] = 0,
|
||||
dry_run: bool | Literal[0, 1] = 0,
|
||||
) -> None: ...
|
||||
def find_executable(executable: str, path: str | None = None) -> str | None: ...
|
||||
|
||||
@@ -3,7 +3,7 @@ import types
|
||||
import unittest
|
||||
from _typeshed import ExcInfo
|
||||
from collections.abc import Callable
|
||||
from typing import Any, ClassVar, NamedTuple
|
||||
from typing import Any, NamedTuple, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
__all__ = [
|
||||
@@ -42,17 +42,15 @@ __all__ = [
|
||||
"debug",
|
||||
]
|
||||
|
||||
# MyPy errors on conditionals within named tuples.
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
class TestResults(NamedTuple):
|
||||
def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... # type: ignore[misc]
|
||||
skipped: int
|
||||
@type_check_only
|
||||
class _TestResultsBase(NamedTuple):
|
||||
failed: int
|
||||
attempted: int
|
||||
_fields: ClassVar = ("failed", "attempted") # type: ignore[misc]
|
||||
__match_args__ = ("failed", "attempted") # type: ignore[misc]
|
||||
__doc__: None # type: ignore[misc]
|
||||
|
||||
class TestResults(_TestResultsBase):
|
||||
def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ...
|
||||
skipped: int
|
||||
|
||||
else:
|
||||
class TestResults(NamedTuple):
|
||||
|
||||
@@ -4,6 +4,29 @@ from email.policy import Policy
|
||||
from typing import IO
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# At runtime, listing submodules in __all__ without them being imported is
|
||||
# valid, and causes them to be included in a star import. See #6523
|
||||
|
||||
__all__ = [ # noqa: F822 # Undefined names in __all__
|
||||
"base64mime", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"charset", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"encoders", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"errors", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"feedparser", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"generator", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"header", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"iterators", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"message", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"message_from_file",
|
||||
"message_from_binary_file",
|
||||
"message_from_string",
|
||||
"message_from_bytes",
|
||||
"mime", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"parser", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"quoprimime", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"utils", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
]
|
||||
|
||||
# Definitions imported by multiple submodules in typeshed
|
||||
_ParamType: TypeAlias = str | tuple[str | None, str | None, str] # noqa: Y047
|
||||
_ParamsType: TypeAlias = str | None | tuple[str, str | None, str] # noqa: Y047
|
||||
@@ -12,18 +35,3 @@ def message_from_string(s: str, _class: Callable[[], Message] = ..., *, policy:
|
||||
def message_from_bytes(s: bytes | bytearray, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ...
|
||||
def message_from_file(fp: IO[str], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ...
|
||||
def message_from_binary_file(fp: IO[bytes], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ...
|
||||
|
||||
# Names in __all__ with no definition:
|
||||
# base64mime
|
||||
# charset
|
||||
# encoders
|
||||
# errors
|
||||
# feedparser
|
||||
# generator
|
||||
# header
|
||||
# iterators
|
||||
# message
|
||||
# mime
|
||||
# parser
|
||||
# quoprimime
|
||||
# utils
|
||||
|
||||
@@ -5,6 +5,8 @@ from email.message import Message
|
||||
from typing import Generic, Protocol, TypeVar, type_check_only
|
||||
from typing_extensions import Self
|
||||
|
||||
__all__ = ["Policy", "Compat32", "compat32"]
|
||||
|
||||
_MessageT = TypeVar("_MessageT", bound=Message, default=Message)
|
||||
|
||||
@type_check_only
|
||||
|
||||
1
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/aliases.pyi
vendored
Normal file
1
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/aliases.pyi
vendored
Normal file
@@ -0,0 +1 @@
|
||||
aliases: dict[str, str]
|
||||
30
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/ascii.pyi
vendored
Normal file
30
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/ascii.pyi
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import codecs
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
# At runtime, this is codecs.ascii_encode
|
||||
@staticmethod
|
||||
def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
# At runtime, this is codecs.ascii_decode
|
||||
@staticmethod
|
||||
def decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter): ...
|
||||
class StreamReader(Codec, codecs.StreamReader): ...
|
||||
|
||||
# Note: encode being a decode function and decode being an encode function is accurate to runtime.
|
||||
class StreamConverter(StreamWriter, StreamReader): # type: ignore[misc] # incompatible methods in base classes
|
||||
# At runtime, this is codecs.ascii_decode
|
||||
@staticmethod
|
||||
def encode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... # type: ignore[override]
|
||||
# At runtime, this is codecs.ascii_encode
|
||||
@staticmethod
|
||||
def decode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/base64_codec.pyi
vendored
Normal file
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/base64_codec.pyi
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import codecs
|
||||
from _typeshed import ReadableBuffer
|
||||
from typing import ClassVar
|
||||
|
||||
# This codec is bytes to bytes.
|
||||
|
||||
def base64_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def base64_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
class StreamReader(Codec, codecs.StreamReader):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5.pyi
vendored
Normal file
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5.pyi
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import _multibytecodec as mbc
|
||||
import codecs
|
||||
from typing import ClassVar
|
||||
|
||||
codec: mbc._MultibyteCodec
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
|
||||
class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5hkscs.pyi
vendored
Normal file
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5hkscs.pyi
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import _multibytecodec as mbc
|
||||
import codecs
|
||||
from typing import ClassVar
|
||||
|
||||
codec: mbc._MultibyteCodec
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
|
||||
class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/bz2_codec.pyi
vendored
Normal file
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/bz2_codec.pyi
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import codecs
|
||||
from _typeshed import ReadableBuffer
|
||||
from typing import ClassVar
|
||||
|
||||
# This codec is bytes to bytes.
|
||||
|
||||
def bz2_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def bz2_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
class StreamReader(Codec, codecs.StreamReader):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
33
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/charmap.pyi
vendored
Normal file
33
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/charmap.pyi
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import codecs
|
||||
from _codecs import _CharMap
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
# At runtime, this is codecs.charmap_encode
|
||||
@staticmethod
|
||||
def encode(str: str, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[bytes, int]: ...
|
||||
# At runtime, this is codecs.charmap_decode
|
||||
@staticmethod
|
||||
def decode(data: ReadableBuffer, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, stream: codecs._WritableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
class StreamReader(Codec, codecs.StreamReader):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, stream: codecs._ReadableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override]
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp037.pyi
vendored
Normal file
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp037.pyi
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import codecs
|
||||
from _codecs import _EncodingMap
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter): ...
|
||||
class StreamReader(Codec, codecs.StreamReader): ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
|
||||
decoding_table: str
|
||||
encoding_table: _EncodingMap
|
||||
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1006.pyi
vendored
Normal file
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1006.pyi
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import codecs
|
||||
from _codecs import _EncodingMap
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter): ...
|
||||
class StreamReader(Codec, codecs.StreamReader): ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
|
||||
decoding_table: str
|
||||
encoding_table: _EncodingMap
|
||||
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1026.pyi
vendored
Normal file
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1026.pyi
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import codecs
|
||||
from _codecs import _EncodingMap
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter): ...
|
||||
class StreamReader(Codec, codecs.StreamReader): ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
|
||||
decoding_table: str
|
||||
encoding_table: _EncodingMap
|
||||
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1125.pyi
vendored
Normal file
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1125.pyi
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import codecs
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter): ...
|
||||
class StreamReader(Codec, codecs.StreamReader): ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
|
||||
decoding_map: dict[int, int | None]
|
||||
decoding_table: str
|
||||
encoding_map: dict[int, int]
|
||||
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1140.pyi
vendored
Normal file
21
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/cp1140.pyi
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import codecs
|
||||
from _codecs import _EncodingMap
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter): ...
|
||||
class StreamReader(Codec, codecs.StreamReader): ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
|
||||
decoding_table: str
|
||||
encoding_table: _EncodingMap
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user