Compare commits

..

2 Commits

Author SHA1 Message Date
David Peter
36ba9707ca Remove TODO message 2024-11-27 13:19:23 +01:00
David Peter
4aba390838 [red-knot] No panic for illegal self-referential f-string annotations 2024-11-27 10:33:22 +01:00
1096 changed files with 6345 additions and 17278 deletions

5
.github/CODEOWNERS vendored
View File

@@ -13,10 +13,9 @@
# flake8-pyi
/crates/ruff_linter/src/rules/flake8_pyi/ @AlexWaygood
# Script for fuzzing the parser/red-knot etc.
/python/py-fuzzer/ @AlexWaygood
# Script for fuzzing the parser
/scripts/fuzz-parser/ @AlexWaygood
# red-knot
/crates/red_knot* @carljm @MichaReiser @AlexWaygood @sharkdp
/crates/ruff_db/ @carljm @MichaReiser @AlexWaygood @sharkdp
/scripts/knot_benchmark/ @carljm @MichaReiser @AlexWaygood @sharkdp

View File

@@ -49,7 +49,7 @@ jobs:
- crates/ruff_text_size/**
- crates/ruff_python_ast/**
- crates/ruff_python_parser/**
- python/py-fuzzer/**
- scripts/fuzz-parser/**
- .github/workflows/ci.yaml
linter:
@@ -82,7 +82,6 @@ jobs:
code:
- "**/*"
- "!**/*.md"
- "crates/red_knot_python_semantic/resources/mdtest/**/*.md"
- "!docs/**"
- "!assets/**"
@@ -318,7 +317,13 @@ jobs:
FORCE_COLOR: 1
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install Python requirements
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
- uses: actions/download-artifact@v4
name: Download Ruff binary to test
id: download-cached-binary
@@ -330,15 +335,7 @@ jobs:
# Make executable, since artifact download doesn't preserve this
chmod +x ${{ steps.download-cached-binary.outputs.download-path }}/ruff
(
uvx \
--python=${{ env.PYTHON_VERSION }} \
--from=./python/py-fuzzer \
fuzz \
--test-executable=${{ steps.download-cached-binary.outputs.download-path }}/ruff \
--bin=ruff \
0-500
)
python scripts/fuzz-parser/fuzz.py --bin ruff 0-500 --test-executable ${{ steps.download-cached-binary.outputs.download-path }}/ruff
scripts:
name: "test scripts"
@@ -383,7 +380,7 @@ jobs:
name: ruff
path: target/debug
- uses: dawidd6/action-download-artifact@v7
- uses: dawidd6/action-download-artifact@v6
name: Download baseline Ruff binary
with:
name: ruff
@@ -559,7 +556,7 @@ jobs:
- name: "Install Rust toolchain"
run: rustup show
- name: Install uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v3
- uses: Swatinem/rust-cache@v2
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}

View File

@@ -32,7 +32,13 @@ jobs:
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install Python requirements
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
- name: "Install Rust toolchain"
run: rustup show
- name: "Install mold"
@@ -43,16 +49,7 @@ jobs:
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
run: cargo build --locked
- name: Fuzz
run: |
(
uvx \
--python=3.12 \
--from=./python/py-fuzzer \
fuzz \
--test-executable=target/debug/ruff \
--bin=ruff \
$(shuf -i 0-9999999999999999999 -n 1000)
)
run: python scripts/fuzz-parser/fuzz.py --bin ruff $(shuf -i 0-9999999999999999999 -n 1000) --test-executable target/debug/ruff
create-issue-on-failure:
name: Create an issue if the daily fuzz surfaced any bugs

View File

@@ -17,7 +17,7 @@ jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: dawidd6/action-download-artifact@v7
- uses: dawidd6/action-download-artifact@v6
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@v7
- uses: dawidd6/action-download-artifact@v6
name: "Download ecosystem results"
id: download-ecosystem-result
if: steps.pr-number.outputs.pr-number

View File

@@ -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.13.0
uses: cloudflare/wrangler-action@v3.12.1
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}

View File

@@ -22,7 +22,7 @@ jobs:
id-token: write
steps:
- name: "Install uv"
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v3
- uses: actions/download-artifact@v4
with:
pattern: wheels-*

View File

@@ -22,7 +22,7 @@ repos:
- id: validate-pyproject
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.19
rev: 0.7.18
hooks:
- id: mdformat
additional_dependencies:
@@ -36,7 +36,7 @@ repos:
)$
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.43.0
rev: v0.42.0
hooks:
- id: markdownlint-fix
exclude: |
@@ -59,7 +59,7 @@ repos:
- black==24.10.0
- repo: https://github.com/crate-ci/typos
rev: v1.28.1
rev: v1.27.3
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.8.1
rev: v0.7.4
hooks:
- id: ruff-format
- id: ruff
@@ -83,7 +83,7 @@ repos:
# Prettier
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.4.1
rev: v3.3.3
hooks:
- id: prettier
types: [yaml]

View File

@@ -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,

View File

@@ -1,46 +1,5 @@
# Changelog
## 0.8.1
### Preview features
- Formatter: Avoid invalid syntax for format-spec with quotes for all Python versions ([#14625](https://github.com/astral-sh/ruff/pull/14625))
- Formatter: Consider quotes inside format-specs when choosing the quotes for an f-string ([#14493](https://github.com/astral-sh/ruff/pull/14493))
- Formatter: Do not consider f-strings with escaped newlines as multiline ([#14624](https://github.com/astral-sh/ruff/pull/14624))
- Formatter: Fix f-string formatting in assignment statement ([#14454](https://github.com/astral-sh/ruff/pull/14454))
- Formatter: Fix unnecessary space around power operator (`**`) in overlong f-string expressions ([#14489](https://github.com/astral-sh/ruff/pull/14489))
- \[`airflow`\] Avoid implicit `schedule` argument to `DAG` and `@dag` (`AIR301`) ([#14581](https://github.com/astral-sh/ruff/pull/14581))
- \[`flake8-builtins`\] Exempt private built-in modules (`A005`) ([#14505](https://github.com/astral-sh/ruff/pull/14505))
- \[`flake8-pytest-style`\] Fix `pytest.mark.parametrize` rules to check calls instead of decorators ([#14515](https://github.com/astral-sh/ruff/pull/14515))
- \[`flake8-type-checking`\] Implement `runtime-cast-value` (`TC006`) ([#14511](https://github.com/astral-sh/ruff/pull/14511))
- \[`flake8-type-checking`\] Implement `unquoted-type-alias` (`TC007`) and `quoted-type-alias` (`TC008`) ([#12927](https://github.com/astral-sh/ruff/pull/12927))
- \[`flake8-use-pathlib`\] Recommend `Path.iterdir()` over `os.listdir()` (`PTH208`) ([#14509](https://github.com/astral-sh/ruff/pull/14509))
- \[`pylint`\] Extend `invalid-envvar-default` to detect `os.environ.get` (`PLW1508`) ([#14512](https://github.com/astral-sh/ruff/pull/14512))
- \[`pylint`\] Implement `len-test` (`PLC1802`) ([#14309](https://github.com/astral-sh/ruff/pull/14309))
- \[`refurb`\] Fix bug where methods defined using lambdas were flagged by `FURB118` ([#14639](https://github.com/astral-sh/ruff/pull/14639))
- \[`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
- Ignore more rules for stub files ([#14541](https://github.com/astral-sh/ruff/pull/14541))
- \[`pep8-naming`\] Eliminate false positives for single-letter names (`N811`, `N814`) ([#14584](https://github.com/astral-sh/ruff/pull/14584))
- \[`pyflakes`\] Avoid false positives in `@no_type_check` contexts (`F821`, `F722`) ([#14615](https://github.com/astral-sh/ruff/pull/14615))
- \[`ruff`\] Detect redirected-noqa in file-level comments (`RUF101`) ([#14635](https://github.com/astral-sh/ruff/pull/14635))
- \[`ruff`\] Mark fixes for `unsorted-dunder-all` and `unsorted-dunder-slots` as unsafe when there are complex comments in the sequence (`RUF022`, `RUF023`) ([#14560](https://github.com/astral-sh/ruff/pull/14560))
### Bug fixes
- 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))
## 0.8.0
Check out the [blog post](https://astral.sh/blog/ruff-v0.8.0) for a migration guide and overview of the changes!
@@ -257,7 +216,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))
@@ -1272,7 +1231,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))
@@ -1647,7 +1606,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

View File

@@ -139,7 +139,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
1. Create a file for your rule (e.g., `crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs`).
1. In that file, define a violation struct (e.g., `pub struct AssertFalse`). You can grep for
`#[derive(ViolationMetadata)]` to see examples.
`#[violation]` to see examples.
1. In that file, define a function that adds the violation to the diagnostic list as appropriate
(e.g., `pub(crate) fn assert_false`) based on whatever inputs are required for the rule (e.g.,
@@ -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`).

252
Cargo.lock generated
View File

@@ -413,7 +413,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -693,7 +693,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -704,7 +704,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -774,7 +774,7 @@ dependencies = [
"glob",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -826,7 +826,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -1246,7 +1246,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -1314,9 +1314,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.7.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
@@ -1420,7 +1420,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -1485,11 +1485,10 @@ checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
[[package]]
name = "js-sys"
version = "0.3.74"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
dependencies = [
"once_cell",
"wasm-bindgen",
]
@@ -1521,9 +1520,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.167"
version = "0.2.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
[[package]]
name = "libcst"
@@ -1547,7 +1546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ae40017ac09cd2c6a53504cb3c871c7f2b41466eac5bc66ba63f39073b467b"
dependencies = [
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -1834,9 +1833,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordermap"
version = "0.5.4"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f80a48eb68b6a7da9829b8b0429011708f775af80676a91063d023a66a656106"
checksum = "31f2bd7b03bf2c767e1bb7b91505dbe022833776e60480275e6f2fb0db0c7503"
dependencies = [
"indexmap",
]
@@ -1911,9 +1910,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
[[package]]
name = "pathdiff"
version = "0.2.3"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
[[package]]
name = "peg"
@@ -1966,7 +1965,7 @@ dependencies = [
"once_cell",
"pep440_rs",
"regex",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"serde",
"smallvec",
"thiserror 1.0.67",
@@ -2013,7 +2012,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -2173,26 +2172,6 @@ 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"
@@ -2293,8 +2272,6 @@ dependencies = [
"itertools 0.13.0",
"memchr",
"ordermap",
"quickcheck",
"quickcheck_macros",
"red_knot_test",
"red_knot_vendored",
"ruff_db",
@@ -2305,7 +2282,7 @@ dependencies = [
"ruff_python_stdlib",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"salsa",
"serde",
"smallvec",
@@ -2332,7 +2309,7 @@ dependencies = [
"ruff_python_ast",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"serde",
"serde_json",
"shellexpand",
@@ -2345,7 +2322,6 @@ name = "red_knot_test"
version = "0.0.0"
dependencies = [
"anyhow",
"camino",
"colored",
"memchr",
"red_knot_python_semantic",
@@ -2356,7 +2332,7 @@ dependencies = [
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"salsa",
"smallvec",
]
@@ -2403,7 +2379,8 @@ dependencies = [
"ruff_cache",
"ruff_db",
"ruff_python_ast",
"rustc-hash 2.1.0",
"ruff_text_size",
"rustc-hash 2.0.0",
"salsa",
"serde",
"thiserror 2.0.3",
@@ -2512,7 +2489,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.8.1"
version = "0.8.0"
dependencies = [
"anyhow",
"argfile",
@@ -2552,7 +2529,7 @@ dependencies = [
"ruff_source_file",
"ruff_text_size",
"ruff_workspace",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"serde",
"serde_json",
"shellexpand",
@@ -2583,7 +2560,7 @@ dependencies = [
"ruff_python_formatter",
"ruff_python_parser",
"ruff_python_trivia",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"serde",
"serde_json",
"tikv-jemallocator",
@@ -2625,10 +2602,11 @@ dependencies = [
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"salsa",
"serde",
"tempfile",
"thiserror 2.0.3",
"tracing",
"tracing-subscriber",
"tracing-tree",
@@ -2693,7 +2671,7 @@ dependencies = [
"ruff_cache",
"ruff_macros",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"schemars",
"serde",
"static_assertions",
@@ -2730,7 +2708,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.8.1"
version = "0.8.0"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.2",
@@ -2771,7 +2749,7 @@ dependencies = [
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"schemars",
"serde",
"serde_json",
@@ -2797,7 +2775,7 @@ dependencies = [
"proc-macro2",
"quote",
"ruff_python_trivia",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -2833,7 +2811,7 @@ dependencies = [
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"schemars",
"serde",
]
@@ -2879,7 +2857,7 @@ dependencies = [
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"schemars",
"serde",
"serde_json",
@@ -2926,7 +2904,7 @@ dependencies = [
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"static_assertions",
"unicode-ident",
"unicode-normalization",
@@ -2957,7 +2935,7 @@ dependencies = [
"ruff_python_parser",
"ruff_python_stdlib",
"ruff_text_size",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"schemars",
"serde",
]
@@ -3015,7 +2993,7 @@ dependencies = [
"ruff_source_file",
"ruff_text_size",
"ruff_workspace",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"serde",
"serde_json",
"shellexpand",
@@ -3045,7 +3023,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.8.1"
version = "0.8.0"
dependencies = [
"console_error_panic_hook",
"console_log",
@@ -3096,7 +3074,7 @@ dependencies = [
"ruff_python_semantic",
"ruff_python_stdlib",
"ruff_source_file",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"schemars",
"serde",
"shellexpand",
@@ -3123,9 +3101,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.1.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustix"
@@ -3142,9 +3120,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.19"
version = "0.23.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
dependencies = [
"log",
"once_cell",
@@ -3157,15 +3135,15 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.10.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
[[package]]
name = "rustls-webpki"
version = "0.102.8"
version = "0.102.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
dependencies = [
"ring",
"rustls-pki-types",
@@ -3187,7 +3165,7 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "salsa"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=e68679b3a9c2b5cfd8eab92de89edf4073b03601#e68679b3a9c2b5cfd8eab92de89edf4073b03601"
source = "git+https://github.com/salsa-rs/salsa.git?rev=254c749b02cde2fd29852a7463a33e800b771758#254c749b02cde2fd29852a7463a33e800b771758"
dependencies = [
"append-only-vec",
"arc-swap",
@@ -3197,8 +3175,7 @@ dependencies = [
"indexmap",
"lazy_static",
"parking_lot",
"rayon",
"rustc-hash 2.1.0",
"rustc-hash 2.0.0",
"salsa-macro-rules",
"salsa-macros",
"smallvec",
@@ -3208,17 +3185,17 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
version = "0.1.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=e68679b3a9c2b5cfd8eab92de89edf4073b03601#e68679b3a9c2b5cfd8eab92de89edf4073b03601"
source = "git+https://github.com/salsa-rs/salsa.git?rev=254c749b02cde2fd29852a7463a33e800b771758#254c749b02cde2fd29852a7463a33e800b771758"
[[package]]
name = "salsa-macros"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=e68679b3a9c2b5cfd8eab92de89edf4073b03601#e68679b3a9c2b5cfd8eab92de89edf4073b03601"
source = "git+https://github.com/salsa-rs/salsa.git?rev=254c749b02cde2fd29852a7463a33e800b771758#254c749b02cde2fd29852a7463a33e800b771758"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
"synstructure",
]
@@ -3252,7 +3229,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3301,7 +3278,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3312,7 +3289,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3335,7 +3312,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3376,7 +3353,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3410,9 +3387,9 @@ dependencies = [
[[package]]
name = "similar"
version = "2.6.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
[[package]]
name = "siphasher"
@@ -3484,7 +3461,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3495,20 +3472,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.109"
version = "2.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
dependencies = [
"proc-macro2",
"quote",
@@ -3523,7 +3489,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3586,7 +3552,7 @@ dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3597,7 +3563,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
"test-case-core",
]
@@ -3627,7 +3593,7 @@ checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3638,7 +3604,7 @@ checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -3742,9 +3708,9 @@ dependencies = [
[[package]]
name = "tracing"
version = "0.1.41"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"log",
"pin-project-lite",
@@ -3754,20 +3720,20 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.28"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.33"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
@@ -3809,9 +3775,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"matchers",
"nu-ansi-term 0.46.0",
@@ -3960,21 +3926,18 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.11.0"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30e6f97efe1fa43535ee241ee76967d3ff6ff3953ebb430d8d55c5393029e7b"
checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a"
dependencies = [
"base64 0.22.0",
"flate2",
"litemap",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"url",
"webpki-roots",
"yoke",
"zerofrom",
]
[[package]]
@@ -4033,7 +3996,7 @@ checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -4108,9 +4071,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.97"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
dependencies = [
"cfg-if",
"once_cell",
@@ -4119,37 +4082,36 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.97"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.47"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d"
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
dependencies = [
"cfg-if",
"js-sys",
"once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.97"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -4157,32 +4119,32 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.97"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.97"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.47"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d919bb60ebcecb9160afee6c71b43a58a4f0517a2de0054cd050d02cec08201"
checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426"
dependencies = [
"console_error_panic_hook",
"js-sys",
"minicov",
"once_cell",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -4191,20 +4153,20 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.47"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222ebde6ea87fbfa6bdd2e9f1fd8a91d60aee5db68792632176c4e16a74fc7d8"
checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
name = "web-sys"
version = "0.3.74"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -4500,7 +4462,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
"synstructure",
]
@@ -4521,7 +4483,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]
@@ -4541,7 +4503,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
"synstructure",
]
@@ -4570,7 +4532,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn",
]
[[package]]

View File

@@ -118,7 +118,7 @@ rand = { version = "0.8.5" }
rayon = { version = "1.10.0" }
regex = { version = "1.10.2" }
rustc-hash = { version = "2.0.0" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "e68679b3a9c2b5cfd8eab92de89edf4073b03601" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "254c749b02cde2fd29852a7463a33e800b771758" }
schemars = { version = "0.8.16" }
seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }

View File

@@ -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.0/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.8.0/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -170,7 +170,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.8.1
rev: v0.8.0
hooks:
# Run the linter.
- id: ruff

View File

@@ -1,25 +1,21 @@
doc-valid-idents = [
"..",
"CodeQL",
"FastAPI",
"IPython",
"LangChain",
"LibCST",
"McCabe",
"NumPy",
"SCREAMING_SNAKE_CASE",
"SQLAlchemy",
"StackOverflow",
"PyCharm",
"SNMPv1",
"SNMPv2",
"SNMPv3",
"PyFlakes"
"..",
"CodeQL",
"FastAPI",
"IPython",
"LangChain",
"LibCST",
"McCabe",
"NumPy",
"SCREAMING_SNAKE_CASE",
"SQLAlchemy",
"StackOverflow",
"PyCharm",
]
ignore-interior-mutability = [
# Interned is read-only. The wrapped `Rc` never gets updated.
"ruff_formatter::format_element::Interned",
# The expression is read-only.
"ruff_python_ast::hashable::HashableExpr",
# Interned is read-only. The wrapped `Rc` never gets updated.
"ruff_formatter::format_element::Interned",
# The expression is read-only.
"ruff_python_ast::hashable::HashableExpr",
]

View File

@@ -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

View File

@@ -12,7 +12,7 @@ use red_knot_workspace::watch;
use red_knot_workspace::watch::WorkspaceWatcher;
use red_knot_workspace::workspace::settings::Configuration;
use red_knot_workspace::workspace::WorkspaceMetadata;
use ruff_db::diagnostic::CompileDiagnostic;
use ruff_db::diagnostic::Diagnostic;
use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
use salsa::plumbing::ZalsaDatabase;
use target_version::TargetVersion;
@@ -297,7 +297,7 @@ impl MainLoop {
while let Ok(message) = self.receiver.recv() {
match message {
MainLoopMessage::CheckWorkspace => {
let db = db.clone();
let db = db.snapshot();
let sender = self.sender.clone();
// Spawn a new task that checks the workspace. This needs to be done in a separate thread
@@ -380,7 +380,7 @@ impl MainLoopCancellationToken {
enum MainLoopMessage {
CheckWorkspace,
CheckCompleted {
result: Vec<CompileDiagnostic>,
result: Vec<Box<dyn Diagnostic>>,
revision: u64,
},
ApplyChanges(Vec<watch::ChangeEvent>),

View File

@@ -1,23 +1,25 @@
#![allow(clippy::disallowed_names)]
use std::io::Write;
use std::time::{Duration, Instant};
use std::time::Duration;
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::{directory_watcher, ChangeEvent, WorkspaceWatcher};
use red_knot_workspace::watch;
use red_knot_workspace::watch::{directory_watcher, 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<ChangeEvent>>,
changes_receiver: crossbeam::channel::Receiver<Vec<watch::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,
@@ -38,36 +40,12 @@ impl TestCase {
&self.db
}
#[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 stop_watch(&mut self) -> Vec<watch::ChangeEvent> {
self.try_stop_watch(Duration::from_secs(10))
.expect("Expected watch changes but observed none")
}
fn try_stop_watch<M>(
&mut self,
mut matcher: M,
timeout: Duration,
) -> Result<Vec<ChangeEvent>, Vec<ChangeEvent>>
where
M: MatchEvent,
{
fn try_stop_watch(&mut self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
tracing::debug!("Try stopping watch with timeout {:?}", timeout);
let watcher = self
@@ -75,50 +53,36 @@ impl TestCase {
.take()
.expect("Cannot call `stop_watch` more than once");
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);
}
}
let mut all_events = self
.changes_receiver
.recv_timeout(timeout)
.unwrap_or_default();
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);
}
Ok(all_events)
if all_events.is_empty() {
return None;
}
Some(all_events)
}
fn take_watch_changes(&self) -> Vec<ChangeEvent> {
fn take_watch_changes(&self) -> Vec<watch::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<ChangeEvent>> {
let watcher = self.watcher.as_ref()?;
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
let Some(watcher) = &self.watcher else {
return None;
};
let mut all_events = self
.changes_receiver
@@ -140,7 +104,7 @@ impl TestCase {
Some(all_events)
}
fn apply_changes(&mut self, changes: Vec<ChangeEvent>) {
fn apply_changes(&mut self, changes: Vec<watch::ChangeEvent>) {
self.db.apply_changes(changes, Some(&self.configuration));
}
@@ -176,23 +140,6 @@ 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<()>;
}
@@ -368,7 +315,7 @@ fn new_file() -> anyhow::Result<()> {
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
let changes = case.stop_watch(event_for_file("foo.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -391,7 +338,7 @@ fn new_ignored_file() -> anyhow::Result<()> {
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
let changes = case.stop_watch(event_for_file("foo.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -413,7 +360,7 @@ fn changed_file() -> anyhow::Result<()> {
update_file(&foo_path, "print('Version 2')")?;
let changes = case.stop_watch(event_for_file("foo.py"));
let changes = case.stop_watch();
assert!(!changes.is_empty());
@@ -438,7 +385,7 @@ fn deleted_file() -> anyhow::Result<()> {
std::fs::remove_file(foo_path.as_std_path())?;
let changes = case.stop_watch(event_for_file("foo.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -470,7 +417,7 @@ fn move_file_to_trash() -> anyhow::Result<()> {
trash_path.join("foo.py").as_std_path(),
)?;
let changes = case.stop_watch(event_for_file("foo.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -502,7 +449,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(event_for_file("foo.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -530,7 +477,7 @@ fn rename_file() -> anyhow::Result<()> {
std::fs::rename(foo_path.as_std_path(), bar_path.as_std_path())?;
let changes = case.stop_watch(event_for_file("bar.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -574,7 +521,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(event_for_file("sub"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -633,7 +580,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(event_for_file("sub"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -657,6 +604,8 @@ 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", ""),
@@ -695,8 +644,11 @@ 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")?;
// 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"));
let changes = case.stop_watch();
for event in &changes {
tracing::debug!("Event: {:?}", event);
}
case.apply_changes(changes);
@@ -769,7 +721,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(event_for_file("sub"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -806,7 +758,7 @@ fn search_path() -> anyhow::Result<()> {
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
let changes = case.stop_watch(event_for_file("a.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -837,7 +789,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(event_for_file("a.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -866,9 +818,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(|_: &ChangeEvent| true, Duration::from_millis(100));
let changes = case.try_stop_watch(Duration::from_millis(100));
assert_eq!(changes, Err(vec![]));
assert_eq!(changes, None);
Ok(())
}
@@ -906,7 +858,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
"os: 3.0-",
)?;
let changes = case.stop_watch(event_for_file("VERSIONS"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -959,7 +911,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(event_for_file("foo.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1030,7 +982,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(ChangeEvent::is_changed);
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1069,7 +1021,7 @@ mod unix {
)
.with_context(|| "Failed to set file permissions.")?;
let changes = case.stop_watch(event_for_file("foo.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1167,7 +1119,7 @@ mod unix {
update_file(baz_workspace, "def baz(): print('Version 3')")
.context("Failed to update bar/baz.py")?;
let changes = case.stop_watch(event_for_file("baz.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1238,7 +1190,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(event_for_file("baz.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1346,7 +1298,7 @@ mod unix {
update_file(&baz_original, "def baz(): print('Version 2')")
.context("Failed to update bar/baz.py")?;
let changes = case.stop_watch(event_for_file("baz.py"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1400,7 +1352,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(ChangeEvent::is_deleted);
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1412,6 +1364,7 @@ fn nested_packages_delete_root() -> anyhow::Result<()> {
#[test]
fn added_package() -> anyhow::Result<()> {
let _ = setup_logging();
let mut case = setup([
(
"pyproject.toml",
@@ -1453,7 +1406,7 @@ fn added_package() -> anyhow::Result<()> {
)
.context("failed to write pyproject.toml for package b")?;
let changes = case.stop_watch(event_for_file("pyproject.toml"));
let changes = case.stop_watch();
case.apply_changes(changes);
@@ -1496,7 +1449,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(ChangeEvent::is_deleted);
let changes = case.stop_watch();
case.apply_changes(changes);

View File

@@ -49,8 +49,6 @@ 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

View File

@@ -1,128 +0,0 @@
# `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

View File

@@ -125,7 +125,6 @@ def f7() -> "\x69nt":
def f8() -> """int""":
return 1
# error: [annotation-byte-string] "Type expressions cannot use bytes literal"
def f9() -> "b'int'":
return 1

View File

@@ -36,6 +36,8 @@ from typing import Callable
def foo() -> int:
return 42
# TODO: This should be resolved once we understand `Callable` annotations
# error: [annotation-with-invalid-expression]
def decorator(func) -> Callable[[], int]:
return foo

View File

@@ -75,6 +75,7 @@ Literal: _SpecialForm
```py
from other import Literal
# error: [annotation-with-invalid-expression]
a1: Literal[26]
def f():

View File

@@ -256,7 +256,7 @@ class O: ...
class X(O): ...
class Y(O): ...
if returns_bool():
if bool():
foo = Y
else:
foo = object

View File

@@ -1,32 +0,0 @@
## 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
```

View File

@@ -1,64 +0,0 @@
# Consolidating narrowed types after if statement
## After if-else statements, narrowing has no effect if the variable is not mutated in any branch
```py
def optional_int() -> int | None: ...
x = optional_int()
if x is None:
pass
else:
pass
reveal_type(x) # revealed: int | None
```
## Narrowing can have a persistent effect if the variable is mutated in one branch
```py
def optional_int() -> int | None: ...
x = optional_int()
if x is None:
x = 10
else:
pass
reveal_type(x) # revealed: int
```
## An if statement without an explicit `else` branch is equivalent to one with a no-op `else` branch
```py
def optional_int() -> int | None: ...
x = optional_int()
y = optional_int()
if x is None:
x = 0
if y is None:
pass
reveal_type(x) # revealed: int
reveal_type(y) # revealed: int | None
```
## An if-elif without an explicit else branch is equivalent to one with an empty else branch
```py
def optional_int() -> int | None: ...
x = optional_int()
if x is None:
x = 0
elif x > 50:
x = 50
reveal_type(x) # revealed: int
```

View File

@@ -21,11 +21,10 @@ reveal_type(Identity[0]) # revealed: str
## Class getitem union
```py
def bool_instance() -> bool:
return True
flag = True
class UnionClassGetItem:
if bool_instance():
if flag:
def __class_getitem__(cls, item: int) -> str:
return item
@@ -60,10 +59,9 @@ reveal_type(x[0]) # revealed: str | int
## Class getitem with unbound method union
```py
def bool_instance() -> bool:
return True
flag = True
if bool_instance():
if flag:
class Spam:
def __class_getitem__(self, x: int) -> str:
return "foo"
@@ -79,10 +77,9 @@ reveal_type(Spam[42])
## TODO: Class getitem non-class union
```py
def bool_instance() -> bool:
return True
flag = True
if bool_instance():
if flag:
class Eggs:
def __class_getitem__(self, x: int) -> str:
return "foo"

View File

@@ -30,11 +30,10 @@ reveal_type(Identity()[0]) # revealed: int
## Getitem union
```py
def bool_instance() -> bool:
return True
flag = True
class Identity:
if bool_instance():
if flag:
def __getitem__(self, index: int) -> int:
return index

View File

@@ -19,7 +19,6 @@ pub(crate) mod tests {
use super::Db;
#[salsa::db]
#[derive(Clone)]
pub(crate) struct TestDb {
storage: salsa::Storage<Self>,
files: Files,

View File

@@ -416,7 +416,7 @@ impl<'db> Iterator for SearchPathIterator<'db> {
}
}
impl FusedIterator for SearchPathIterator<'_> {}
impl<'db> FusedIterator for SearchPathIterator<'db> {}
/// Represents a single `.pth` file in a `site-packages` directory.
/// One or more lines in a `.pth` file may be a (relative or absolute)

View File

@@ -314,6 +314,10 @@ impl<'db> SemanticIndexBuilder<'db> {
self.current_assignments.last().copied()
}
fn current_assignment_mut(&mut self) -> Option<&mut CurrentAssignment<'db>> {
self.current_assignments.last_mut()
}
fn add_pattern_constraint(
&mut self,
subject: &ast::Expr,
@@ -682,7 +686,7 @@ where
ast::Expr::List(_) | ast::Expr::Tuple(_) => {
Some(CurrentAssignment::Assign {
node,
first: true,
unpack: Some(Unpack::new(
self.db,
self.file,
@@ -696,9 +700,11 @@ where
)),
})
}
ast::Expr::Name(_) => {
Some(CurrentAssignment::Assign { node, unpack: None })
}
ast::Expr::Name(_) => Some(CurrentAssignment::Assign {
node,
unpack: None,
first: false,
}),
_ => None,
};
@@ -763,22 +769,7 @@ where
let mut constraints = vec![constraint];
self.visit_body(&node.body);
let mut post_clauses: Vec<FlowSnapshot> = vec![];
let elif_else_clauses = node
.elif_else_clauses
.iter()
.map(|clause| (clause.test.as_ref(), clause.body.as_slice()));
let has_else = node
.elif_else_clauses
.last()
.is_some_and(|clause| clause.test.is_none());
let elif_else_clauses = elif_else_clauses.chain(if has_else {
// if there's an `else` clause already, we don't need to add another
None
} else {
// if there's no `else` branch, we should add a no-op `else` branch
Some((None, Default::default()))
});
for (clause_test, clause_body) in elif_else_clauses {
for clause in &node.elif_else_clauses {
// snapshot after every block except the last; the last one will just become
// the state that we merge the other snapshots into
post_clauses.push(self.flow_snapshot());
@@ -788,15 +779,24 @@ where
for constraint in &constraints {
self.record_negated_constraint(*constraint);
}
if let Some(elif_test) = clause_test {
if let Some(elif_test) = &clause.test {
self.visit_expr(elif_test);
constraints.push(self.record_expression_constraint(elif_test));
}
self.visit_body(clause_body);
self.visit_body(&clause.body);
}
for post_clause_state in post_clauses {
self.flow_merge(post_clause_state);
}
let has_else = node
.elif_else_clauses
.last()
.is_some_and(|clause| clause.test.is_none());
if !has_else {
// if there's no else clause, then it's possible we took none of the branches,
// and the pre_if state can reach here
self.flow_merge(pre_if);
}
}
ast::Stmt::While(ast::StmtWhile {
test,
@@ -1076,13 +1076,18 @@ where
if is_definition {
match self.current_assignment() {
Some(CurrentAssignment::Assign { node, unpack }) => {
Some(CurrentAssignment::Assign {
node,
first,
unpack,
}) => {
self.add_definition(
symbol,
AssignmentDefinitionNodeRef {
unpack,
value: &node.value,
name: name_node,
first,
},
);
}
@@ -1133,6 +1138,11 @@ where
}
}
if let Some(CurrentAssignment::Assign { first, .. }) = self.current_assignment_mut()
{
*first = false;
}
walk_expr(self, expr);
}
ast::Expr::Named(node) => {
@@ -1339,6 +1349,7 @@ where
enum CurrentAssignment<'a> {
Assign {
node: &'a ast::StmtAssign,
first: bool,
unpack: Option<Unpack<'a>>,
},
AnnAssign(&'a ast::StmtAnnAssign),

View File

@@ -211,6 +211,7 @@ pub(crate) struct AssignmentDefinitionNodeRef<'a> {
pub(crate) unpack: Option<Unpack<'a>>,
pub(crate) value: &'a ast::Expr,
pub(crate) name: &'a ast::ExprName,
pub(crate) first: bool,
}
#[derive(Copy, Clone, Debug)]
@@ -281,10 +282,12 @@ impl<'db> DefinitionNodeRef<'db> {
unpack,
value,
name,
first,
}) => DefinitionKind::Assignment(AssignmentDefinitionKind {
target: TargetKind::from(unpack),
value: AstNodeRef::new(parsed.clone(), value),
name: AstNodeRef::new(parsed, name),
first,
}),
DefinitionNodeRef::AnnotatedAssignment(assign) => {
DefinitionKind::AnnotatedAssignment(AstNodeRef::new(parsed, assign))
@@ -371,6 +374,7 @@ impl<'db> DefinitionNodeRef<'db> {
value: _,
unpack: _,
name,
first: _,
}) => name.into(),
Self::AnnotatedAssignment(node) => node.into(),
Self::AugmentedAssignment(node) => node.into(),
@@ -587,6 +591,7 @@ pub struct AssignmentDefinitionKind<'db> {
target: TargetKind<'db>,
value: AstNodeRef<ast::Expr>,
name: AstNodeRef<ast::ExprName>,
first: bool,
}
impl<'db> AssignmentDefinitionKind<'db> {
@@ -601,6 +606,10 @@ impl<'db> AssignmentDefinitionKind<'db> {
pub(crate) fn name(&self) -> &ast::ExprName {
self.name.node()
}
pub(crate) fn is_first(&self) -> bool {
self.first
}
}
#[derive(Clone, Debug)]

View File

@@ -400,7 +400,7 @@ pub(crate) struct ConstraintsIterator<'map, 'db> {
constraint_ids: ConstraintIdIterator<'map>,
}
impl<'db> Iterator for ConstraintsIterator<'_, 'db> {
impl<'map, 'db> Iterator for ConstraintsIterator<'map, 'db> {
type Item = Constraint<'db>;
fn next(&mut self) -> Option<Self::Item> {
@@ -424,7 +424,7 @@ impl DeclarationsIterator<'_, '_> {
}
}
impl<'db> Iterator for DeclarationsIterator<'_, 'db> {
impl<'map, 'db> Iterator for DeclarationsIterator<'map, 'db> {
type Item = Definition<'db>;
fn next(&mut self) -> Option<Self::Item> {

View File

@@ -401,7 +401,7 @@ pub(super) struct DeclarationIdIterator<'a> {
inner: DeclarationsIterator<'a>,
}
impl Iterator for DeclarationIdIterator<'_> {
impl<'a> Iterator for DeclarationIdIterator<'a> {
type Item = ScopedDefinitionId;
fn next(&mut self) -> Option<Self::Item> {

View File

@@ -7,6 +7,7 @@ use ruff_db::files::File;
use ruff_python_ast as ast;
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
pub use self::diagnostic::{TypeCheckDiagnostic, TypeCheckDiagnostics};
pub(crate) use self::display::TypeArrayDisplay;
pub(crate) use self::infer::{
infer_deferred_types, infer_definition_types, infer_expression_types, infer_scope_types,
@@ -24,9 +25,7 @@ use crate::stdlib::{
builtins_symbol, core_module_symbol, typing_extensions_symbol, CoreStdlibModule,
};
use crate::symbol::{Boundness, Symbol};
use crate::types::diagnostic::{
report_not_iterable, report_not_iterable_possibly_unbound, report_type_diagnostic,
};
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};
@@ -41,20 +40,21 @@ mod signatures;
mod string_annotation;
mod unpacker;
#[cfg(test)]
mod property_tests;
#[salsa::tracked]
pub fn check_types(db: &dyn Db, file: File) {
#[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();
tracing::debug!("Checking file '{path}'", path = file.path(db));
let index = semantic_index(db, file);
let mut diagnostics = TypeCheckDiagnostics::default();
for scope_id in index.scope_ids() {
let _ = infer_scope_types(db, scope_id);
let result = infer_scope_types(db, scope_id);
diagnostics.extend(result.diagnostics());
}
diagnostics
}
/// Infer the public type of a symbol (its type as seen from outside its scope).
@@ -574,11 +574,8 @@ impl<'db> Type<'db> {
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
}
pub fn tuple<T: Into<Type<'db>>>(
db: &'db dyn Db,
elements: impl IntoIterator<Item = T>,
) -> Self {
TupleType::from_elements(db, elements)
pub fn tuple(db: &'db dyn Db, elements: &[Type<'db>]) -> Self {
Self::Tuple(TupleType::new(db, elements))
}
#[must_use]
@@ -722,6 +719,9 @@ 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,
@@ -1403,7 +1403,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!("call todo")),
Type::Todo(_) => CallOutcome::callable(todo_type!()),
Type::Unknown => CallOutcome::callable(Type::Unknown),
@@ -1556,7 +1556,6 @@ impl<'db> Type<'db> {
Type::KnownInstance(KnownInstanceType::Never | KnownInstanceType::NoReturn) => {
Type::Never
}
Type::KnownInstance(KnownInstanceType::LiteralString) => Type::LiteralString,
_ => todo_type!(),
}
}
@@ -1889,8 +1888,6 @@ 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`)
@@ -1910,7 +1907,6 @@ 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",
@@ -1924,7 +1920,6 @@ impl<'db> KnownInstanceType<'db> {
pub const fn bool(self) -> Truthiness {
match self {
Self::Literal
| Self::LiteralString
| Self::Optional
| Self::TypeVar(_)
| Self::Union
@@ -1938,7 +1933,6 @@ 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",
@@ -1952,7 +1946,6 @@ 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,
@@ -1977,7 +1970,6 @@ impl<'db> KnownInstanceType<'db> {
}
match (module.name().as_str(), instance_name) {
("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),
@@ -2031,7 +2023,7 @@ impl<'db> TypeVarInstance<'db> {
}
#[allow(unused)]
pub(crate) fn constraints(self, db: &'db dyn Db) -> Option<&'db [Type<'db>]> {
pub(crate) fn constraints(self, db: &'db dyn Db) -> Option<&[Type<'db>]> {
if let Some(TypeVarBoundOrConstraints::Constraints(tuple)) = self.bound_or_constraints(db) {
Some(tuple.elements(db))
} else {
@@ -2128,21 +2120,19 @@ impl<'db> CallOutcome<'db> {
}
/// Get the return type of the call, emitting default diagnostics if needed.
fn unwrap_with_diagnostic(
fn unwrap_with_diagnostic<'a>(
&self,
db: &'db dyn Db,
file: File,
node: ast::AnyNodeRef,
diagnostics: &'a mut TypeCheckDiagnosticsBuilder<'db>,
) -> Type<'db> {
match self.return_ty_result(db, file, node) {
match self.return_ty_result(db, node, diagnostics) {
Ok(return_ty) => return_ty,
Err(NotCallableError::Type {
not_callable_ty,
return_ty,
}) => {
report_type_diagnostic(
db,
file,
diagnostics.add(
node,
"call-non-callable",
format_args!(
@@ -2157,9 +2147,7 @@ impl<'db> CallOutcome<'db> {
called_ty,
return_ty,
}) => {
report_type_diagnostic(
db,
file,
diagnostics.add(
node,
"call-non-callable",
format_args!(
@@ -2175,9 +2163,7 @@ impl<'db> CallOutcome<'db> {
called_ty,
return_ty,
}) => {
report_type_diagnostic(
db,
file,
diagnostics.add(
node,
"call-non-callable",
format_args!(
@@ -2192,9 +2178,7 @@ impl<'db> CallOutcome<'db> {
callable_ty: called_ty,
return_ty,
}) => {
report_type_diagnostic(
db,
file,
diagnostics.add(
node,
"call-non-callable",
format_args!(
@@ -2208,11 +2192,11 @@ impl<'db> CallOutcome<'db> {
}
/// Get the return type of the call as a result.
fn return_ty_result(
fn return_ty_result<'a>(
&self,
db: &'db dyn Db,
file: File,
node: ast::AnyNodeRef,
diagnostics: &'a mut TypeCheckDiagnosticsBuilder<'db>,
) -> Result<Type<'db>, NotCallableError<'db>> {
match self {
Self::Callable { return_ty } => Ok(*return_ty),
@@ -2220,9 +2204,7 @@ impl<'db> CallOutcome<'db> {
return_ty,
revealed_ty,
} => {
report_type_diagnostic(
db,
file,
diagnostics.add(
node,
"revealed-type",
format_args!("Revealed type is `{}`", revealed_ty.display(db)),
@@ -2261,10 +2243,10 @@ impl<'db> CallOutcome<'db> {
*return_ty
} else {
revealed = true;
outcome.unwrap_with_diagnostic(db, file, node)
outcome.unwrap_with_diagnostic(db, node, diagnostics)
}
}
_ => outcome.unwrap_with_diagnostic(db, file, node),
_ => outcome.unwrap_with_diagnostic(db, node, diagnostics),
};
union_builder = union_builder.add(return_ty);
}
@@ -2379,21 +2361,20 @@ enum IterationOutcome<'db> {
impl<'db> IterationOutcome<'db> {
fn unwrap_with_diagnostic(
self,
db: &dyn Db,
file: File,
iterable_node: ast::AnyNodeRef,
diagnostics: &mut TypeCheckDiagnosticsBuilder<'db>,
) -> Type<'db> {
match self {
Self::Iterable { element_ty } => element_ty,
Self::NotIterable { not_iterable_ty } => {
report_not_iterable(db, file, iterable_node, not_iterable_ty);
diagnostics.add_not_iterable(iterable_node, not_iterable_ty);
Type::Unknown
}
Self::PossiblyUnboundDunderIter {
iterable_ty,
element_ty,
} => {
report_not_iterable_possibly_unbound(db, file, iterable_node, iterable_ty);
diagnostics.add_not_iterable_possibly_unbound(iterable_node, iterable_ty);
element_ty
}
}
@@ -2586,7 +2567,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) -> &'db [Type<'db>] {
fn explicit_bases(self, db: &'db dyn Db) -> &[Type<'db>] {
self.explicit_bases_query(db)
}
@@ -3014,7 +2995,7 @@ pub struct SliceLiteralType<'db> {
step: Option<i32>,
}
impl SliceLiteralType<'_> {
impl<'db> SliceLiteralType<'db> {
fn as_tuple(self, db: &dyn Db) -> (Option<i32>, Option<i32>, Option<i32>) {
(self.start(db), self.stop(db), self.step(db))
}
@@ -3026,23 +3007,6 @@ 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()
}
@@ -3094,8 +3058,8 @@ pub(crate) mod tests {
/// A test representation of a type that can be transformed unambiguously into a real Type,
/// given a db.
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum Ty {
#[derive(Debug, Clone)]
enum Ty {
Never,
Unknown,
None,
@@ -3119,7 +3083,7 @@ pub(crate) mod tests {
}
impl Ty {
pub(crate) fn into_type(self, db: &TestDb) -> Type<'_> {
fn into_type(self, db: &TestDb) -> Type<'_> {
match self {
Ty::Never => Type::Never,
Ty::Unknown => Type::Unknown,
@@ -3150,21 +3114,13 @@ pub(crate) mod tests {
builder.build()
}
Ty::Tuple(tys) => {
let elements = tys.into_iter().map(|ty| ty.into_type(db));
Type::tuple(db, elements)
let elements: Vec<Type> = tys.into_iter().map(|ty| ty.into_type(db)).collect();
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))]
@@ -3278,7 +3234,6 @@ 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)));

View File

@@ -1,12 +1,16 @@
use crate::types::{ClassLiteralType, Type};
use crate::Db;
use ruff_db::diagnostic::{CompileDiagnostic, Diagnostic, Severity};
use ruff_db::diagnostic::{Diagnostic, Severity};
use ruff_db::files::File;
use ruff_python_ast::{self as ast, AnyNodeRef};
use ruff_text_size::{Ranged, TextRange};
use std::borrow::Cow;
use std::fmt::Formatter;
use std::ops::Deref;
use std::sync::Arc;
#[derive(Debug, Eq, PartialEq, Clone)]
pub(crate) struct TypeCheckDiagnostic {
pub struct TypeCheckDiagnostic {
// TODO: Don't use string keys for rules
pub(super) rule: String,
pub(super) message: String,
@@ -15,15 +19,15 @@ pub(crate) struct TypeCheckDiagnostic {
}
impl TypeCheckDiagnostic {
pub(crate) fn rule(&self) -> &str {
pub fn rule(&self) -> &str {
&self.rule
}
pub(crate) fn message(&self) -> &str {
pub fn message(&self) -> &str {
&self.message
}
pub(crate) fn file(&self) -> File {
pub fn file(&self) -> File {
self.file
}
}
@@ -56,209 +60,263 @@ impl Ranged for TypeCheckDiagnostic {
}
}
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
pub(super) fn report_not_iterable(
db: &dyn Db,
file: File,
node: AnyNodeRef,
not_iterable_ty: Type,
) {
report_type_diagnostic(
db,
file,
node,
"not-iterable",
format_args!(
"Object of type `{}` is not iterable",
not_iterable_ty.display(db)
),
);
/// A collection of type check diagnostics.
///
/// The diagnostics are wrapped in an `Arc` because they need to be cloned multiple times
/// when going from `infer_expression` to `check_file`. We could consider
/// making [`TypeCheckDiagnostic`] a Salsa struct to have them Arena-allocated (once the Tables refactor is done).
/// Using Salsa struct does have the downside that it leaks the Salsa dependency into diagnostics and
/// each Salsa-struct comes with an overhead.
#[derive(Default, Eq, PartialEq)]
pub struct TypeCheckDiagnostics {
inner: Vec<std::sync::Arc<TypeCheckDiagnostic>>,
}
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
/// because its `__iter__` method is possibly unbound.
pub(super) fn report_not_iterable_possibly_unbound(
db: &dyn Db,
file: File,
node: AnyNodeRef,
element_ty: Type,
) {
report_type_diagnostic(
db,
file,
node,
"not-iterable",
format_args!(
"Object of type `{}` is not iterable because its `__iter__` method is possibly unbound",
element_ty.display(db)
),
);
}
impl TypeCheckDiagnostics {
pub(super) fn push(&mut self, diagnostic: TypeCheckDiagnostic) {
self.inner.push(Arc::new(diagnostic));
}
/// Emit a diagnostic declaring that an index is out of bounds for a tuple.
pub(super) fn report_index_out_of_bounds(
db: &dyn Db,
file: File,
kind: &'static str,
node: AnyNodeRef,
tuple_ty: Type,
length: usize,
index: i64,
) {
report_type_diagnostic(
db,
file,
node,
"index-out-of-bounds",
format_args!(
"Index {index} is out of bounds for {kind} `{}` with length {length}",
tuple_ty.display(db)
),
);
}
/// Emit a diagnostic declaring that a type does not support subscripting.
pub(super) fn report_non_subscriptable(
db: &dyn Db,
file: File,
node: AnyNodeRef,
non_subscriptable_ty: Type,
method: &str,
) {
report_type_diagnostic(
db,
file,
node,
"non-subscriptable",
format_args!(
"Cannot subscript object of type `{}` with no `{method}` method",
non_subscriptable_ty.display(db)
),
);
}
pub(super) fn report_unresolved_module<'a>(
db: &dyn Db,
file: File,
import_node: impl Into<AnyNodeRef<'a>>,
level: u32,
module: Option<&str>,
) {
report_type_diagnostic(
db,
file,
import_node.into(),
"unresolved-import",
format_args!(
"Cannot resolve import `{}{}`",
".".repeat(level as usize),
module.unwrap_or_default()
),
);
}
pub(super) fn report_slice_step_size_zero(db: &dyn Db, file: File, node: AnyNodeRef) {
report_type_diagnostic(
db,
file,
node,
"zero-stepsize-in-slice",
format_args!("Slice step size can not be zero"),
);
}
pub(super) fn report_invalid_assignment(
db: &dyn Db,
file: File,
node: AnyNodeRef,
declared_ty: Type,
assigned_ty: Type,
) {
match declared_ty {
Type::ClassLiteral(ClassLiteralType { class }) => {
report_type_diagnostic(db, file, node, "invalid-assignment", format_args!(
"Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional",
class.name(db)));
}
Type::FunctionLiteral(function) => {
report_type_diagnostic(db, file, node, "invalid-assignment", format_args!(
"Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional",
function.name(db)));
}
_ => {
report_type_diagnostic(
db,
file,
node,
"invalid-assignment",
format_args!(
"Object of type `{}` is not assignable to `{}`",
assigned_ty.display(db),
declared_ty.display(db),
),
);
}
pub(crate) fn shrink_to_fit(&mut self) {
self.inner.shrink_to_fit();
}
}
pub(super) fn report_possibly_unresolved_reference(
db: &dyn Db,
file: File,
expr_name_node: &ast::ExprName,
) {
let ast::ExprName { id, .. } = expr_name_node;
report_type_diagnostic(
db,
file,
expr_name_node.into(),
"possibly-unresolved-reference",
format_args!("Name `{id}` used when possibly not defined"),
);
}
pub(super) fn report_unresolved_reference(db: &dyn Db, file: File, expr_name_node: &ast::ExprName) {
let ast::ExprName { id, .. } = expr_name_node;
report_type_diagnostic(
db,
file,
expr_name_node.into(),
"unresolved-reference",
format_args!("Name `{id}` used when not defined"),
);
}
/// Returns `true` if any diagnostic is enabled for this file.
pub(crate) fn is_any_diagnostic_enabled(db: &dyn Db, file: File) -> bool {
db.is_file_open(file)
}
/// Reports a diagnostic for the given node and file if diagnostic reporting is enabled for this file.
pub(crate) fn report_type_diagnostic(
db: &dyn Db,
file: File,
node: AnyNodeRef,
rule: &str,
message: std::fmt::Arguments,
) {
if !is_any_diagnostic_enabled(db, file) {
return;
impl Extend<TypeCheckDiagnostic> for TypeCheckDiagnostics {
fn extend<T: IntoIterator<Item = TypeCheckDiagnostic>>(&mut self, iter: T) {
self.inner.extend(iter.into_iter().map(std::sync::Arc::new));
}
}
// TODO: Don't emit the diagnostic if:
// * The enclosing node contains any syntax errors
// * The rule is disabled for this file. We probably want to introduce a new query that
// returns a rule selector for a given file that respects the package's settings,
// any global pragma comments in the file, and any per-file-ignores.
impl Extend<std::sync::Arc<TypeCheckDiagnostic>> for TypeCheckDiagnostics {
fn extend<T: IntoIterator<Item = Arc<TypeCheckDiagnostic>>>(&mut self, iter: T) {
self.inner.extend(iter);
}
}
CompileDiagnostic::report(
db.upcast(),
TypeCheckDiagnostic {
impl<'a> Extend<&'a std::sync::Arc<TypeCheckDiagnostic>> for TypeCheckDiagnostics {
fn extend<T: IntoIterator<Item = &'a Arc<TypeCheckDiagnostic>>>(&mut self, iter: T) {
self.inner
.extend(iter.into_iter().map(std::sync::Arc::clone));
}
}
impl std::fmt::Debug for TypeCheckDiagnostics {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl Deref for TypeCheckDiagnostics {
type Target = [std::sync::Arc<TypeCheckDiagnostic>];
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl IntoIterator for TypeCheckDiagnostics {
type Item = Arc<TypeCheckDiagnostic>;
type IntoIter = std::vec::IntoIter<std::sync::Arc<TypeCheckDiagnostic>>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a> IntoIterator for &'a TypeCheckDiagnostics {
type Item = &'a Arc<TypeCheckDiagnostic>;
type IntoIter = std::slice::Iter<'a, std::sync::Arc<TypeCheckDiagnostic>>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter()
}
}
pub(super) struct TypeCheckDiagnosticsBuilder<'db> {
db: &'db dyn Db,
file: File,
diagnostics: TypeCheckDiagnostics,
}
impl<'db> TypeCheckDiagnosticsBuilder<'db> {
pub(super) fn new(db: &'db dyn Db, file: File) -> Self {
Self {
db,
file,
diagnostics: TypeCheckDiagnostics::default(),
}
}
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
pub(super) fn add_not_iterable(&mut self, node: AnyNodeRef, not_iterable_ty: Type<'db>) {
self.add(
node,
"not-iterable",
format_args!(
"Object of type `{}` is not iterable",
not_iterable_ty.display(self.db)
),
);
}
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
/// because its `__iter__` method is possibly unbound.
pub(super) fn add_not_iterable_possibly_unbound(
&mut self,
node: AnyNodeRef,
element_ty: Type<'db>,
) {
self.add(
node,
"not-iterable",
format_args!(
"Object of type `{}` is not iterable because its `__iter__` method is possibly unbound",
element_ty.display(self.db)
),
);
}
/// Emit a diagnostic declaring that an index is out of bounds for a tuple.
pub(super) fn add_index_out_of_bounds(
&mut self,
kind: &'static str,
node: AnyNodeRef,
tuple_ty: Type<'db>,
length: usize,
index: i64,
) {
self.add(
node,
"index-out-of-bounds",
format_args!(
"Index {index} is out of bounds for {kind} `{}` with length {length}",
tuple_ty.display(self.db)
),
);
}
/// Emit a diagnostic declaring that a type does not support subscripting.
pub(super) fn add_non_subscriptable(
&mut self,
node: AnyNodeRef,
non_subscriptable_ty: Type<'db>,
method: &str,
) {
self.add(
node,
"non-subscriptable",
format_args!(
"Cannot subscript object of type `{}` with no `{method}` method",
non_subscriptable_ty.display(self.db)
),
);
}
pub(super) fn add_unresolved_module(
&mut self,
import_node: impl Into<AnyNodeRef<'db>>,
level: u32,
module: Option<&str>,
) {
self.add(
import_node.into(),
"unresolved-import",
format_args!(
"Cannot resolve import `{}{}`",
".".repeat(level as usize),
module.unwrap_or_default()
),
);
}
pub(super) fn add_slice_step_size_zero(&mut self, node: AnyNodeRef) {
self.add(
node,
"zero-stepsize-in-slice",
format_args!("Slice step size can not be zero"),
);
}
pub(super) fn add_invalid_assignment(
&mut self,
node: AnyNodeRef,
declared_ty: Type<'db>,
assigned_ty: Type<'db>,
) {
match declared_ty {
Type::ClassLiteral(ClassLiteralType { class }) => {
self.add(node, "invalid-assignment", format_args!(
"Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional",
class.name(self.db)));
}
Type::FunctionLiteral(function) => {
self.add(node, "invalid-assignment", format_args!(
"Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional",
function.name(self.db)));
}
_ => {
self.add(
node,
"invalid-assignment",
format_args!(
"Object of type `{}` is not assignable to `{}`",
assigned_ty.display(self.db),
declared_ty.display(self.db),
),
);
}
}
}
pub(super) fn add_possibly_unresolved_reference(&mut self, expr_name_node: &ast::ExprName) {
let ast::ExprName { id, .. } = expr_name_node;
self.add(
expr_name_node.into(),
"possibly-unresolved-reference",
format_args!("Name `{id}` used when possibly not defined"),
);
}
pub(super) fn add_unresolved_reference(&mut self, expr_name_node: &ast::ExprName) {
let ast::ExprName { id, .. } = expr_name_node;
self.add(
expr_name_node.into(),
"unresolved-reference",
format_args!("Name `{id}` used when not defined"),
);
}
/// Adds a new diagnostic.
///
/// The diagnostic does not get added if the rule isn't enabled for this file.
pub(super) fn add(&mut self, node: AnyNodeRef, rule: &str, message: std::fmt::Arguments) {
if !self.db.is_file_open(self.file) {
return;
}
// TODO: Don't emit the diagnostic if:
// * The enclosing node contains any syntax errors
// * The rule is disabled for this file. We probably want to introduce a new query that
// returns a rule selector for a given file that respects the package's settings,
// any global pragma comments in the file, and any per-file-ignores.
self.diagnostics.push(TypeCheckDiagnostic {
file: self.file,
rule: rule.to_string(),
message: message.to_string(),
range: node.range(),
},
);
});
}
pub(super) fn extend(&mut self, diagnostics: &TypeCheckDiagnostics) {
self.diagnostics.extend(diagnostics);
}
pub(super) fn finish(mut self) -> TypeCheckDiagnostics {
self.diagnostics.shrink_to_fit();
self.diagnostics
}
}

View File

@@ -289,7 +289,7 @@ struct DisplayMaybeNegatedType<'db> {
negated: bool,
}
impl Display for DisplayMaybeNegatedType<'_> {
impl<'db> Display for DisplayMaybeNegatedType<'db> {
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 Display for DisplayTypeArray<'_, '_> {
impl<'db> Display for DisplayTypeArray<'_, 'db> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.join(", ")
.entries(self.types.iter().map(|ty| ty.display(self.db)))

File diff suppressed because it is too large Load Diff

View File

@@ -374,7 +374,6 @@ impl<'db> ClassBase<'db> {
KnownInstanceType::TypeVar(_)
| KnownInstanceType::TypeAliasType(_)
| KnownInstanceType::Literal
| KnownInstanceType::LiteralString
| KnownInstanceType::Union
| KnownInstanceType::NoReturn
| KnownInstanceType::Never

View File

@@ -54,7 +54,6 @@ 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,
@@ -63,7 +62,6 @@ 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,
@@ -72,7 +70,6 @@ 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,
@@ -294,15 +291,8 @@ 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 {
@@ -337,9 +327,6 @@ 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
}
@@ -398,58 +385,46 @@ 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 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] =
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] =
&*expr_call.arguments.args
else {
return None;
};
{
let symbol = self.symbols().symbol_id_by_name(id).unwrap();
let symbol = self.symbols().symbol_id_by_name(id).unwrap();
let class_info_ty =
inference.expression_ty(class_info.scoped_expression_id(self.db, scope));
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)
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)
}
}
};
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,
)
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
}
}
_ => None,
}

View File

@@ -1,237 +0,0 @@
//! 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::{setup_db, Ty};
use crate::db::tests::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)
);
}
/// 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)
);
}

View File

@@ -5,10 +5,10 @@ use ruff_python_ast::{self as ast, ModExpression, StringFlags};
use ruff_python_parser::{parse_expression_range, Parsed};
use ruff_text_size::Ranged;
use crate::types::diagnostic::report_type_diagnostic;
use crate::types::diagnostic::{TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
use crate::Db;
type AnnotationParseResult = Result<Parsed<ModExpression>, ()>;
type AnnotationParseResult = Result<Parsed<ModExpression>, TypeCheckDiagnostics>;
/// Parses the given expression as a string annotation.
pub(crate) fn parse_string_annotation(
@@ -20,13 +20,12 @@ pub(crate) fn parse_string_annotation(
let source = source_text(db.upcast(), file);
let node_text = &source[string_expr.range()];
let mut diagnostics = TypeCheckDiagnosticsBuilder::new(db, file);
if let [string_literal] = string_expr.value.as_slice() {
let prefix = string_literal.flags.prefix();
if prefix.is_raw() {
report_type_diagnostic(
db,
file,
diagnostics.add(
string_literal.into(),
"annotation-raw-string",
format_args!("Type expressions cannot use raw string literal"),
@@ -50,9 +49,7 @@ pub(crate) fn parse_string_annotation(
// ```
match parse_expression_range(source.as_str(), range_excluding_quotes) {
Ok(parsed) => return Ok(parsed),
Err(parse_error) => report_type_diagnostic(
db,
file,
Err(parse_error) => diagnostics.add(
string_literal.into(),
"forward-annotation-syntax-error",
format_args!("Syntax error in forward annotation: {}", parse_error.error),
@@ -61,9 +58,7 @@ pub(crate) fn parse_string_annotation(
} else {
// The raw contents of the string doesn't match the parsed content. This could be the
// case for annotations that contain escape sequences.
report_type_diagnostic(
db,
file,
diagnostics.add(
string_expr.into(),
"annotation-escape-character",
format_args!("Type expressions cannot contain escape characters"),
@@ -71,14 +66,12 @@ pub(crate) fn parse_string_annotation(
}
} else {
// String is implicitly concatenated.
report_type_diagnostic(
db,
file,
diagnostics.add(
string_expr.into(),
"annotation-implicit-concat",
format_args!("Type expressions cannot span multiple string literals"),
);
}
Err(())
Err(diagnostics.finish())
}

View File

@@ -6,22 +6,22 @@ use rustc_hash::FxHashMap;
use crate::semantic_index::ast_ids::{HasScopedExpressionId, ScopedExpressionId};
use crate::semantic_index::symbol::ScopeId;
use crate::types::{todo_type, Type};
use crate::types::{todo_type, Type, TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
use crate::Db;
/// Unpacks the value expression type to their respective targets.
pub(crate) struct Unpacker<'db> {
db: &'db dyn Db,
file: File,
targets: FxHashMap<ScopedExpressionId, Type<'db>>,
diagnostics: TypeCheckDiagnosticsBuilder<'db>,
}
impl<'db> Unpacker<'db> {
pub(crate) fn new(db: &'db dyn Db, file: File) -> Self {
Self {
db,
file,
targets: FxHashMap::default(),
diagnostics: TypeCheckDiagnosticsBuilder::new(db, file),
}
}
@@ -95,7 +95,7 @@ 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,
std::iter::repeat(Type::LiteralString).take(string_literal_ty.len(self.db)),
&vec![Type::LiteralString; string_literal_ty.len(self.db)],
);
self.unpack(target, value_ty, scope);
}
@@ -103,11 +103,9 @@ impl<'db> Unpacker<'db> {
let value_ty = if value_ty.is_literal_string() {
Type::LiteralString
} else {
value_ty.iterate(self.db).unwrap_with_diagnostic(
self.db,
self.file,
AnyNodeRef::from(target),
)
value_ty
.iterate(self.db)
.unwrap_with_diagnostic(AnyNodeRef::from(target), &mut self.diagnostics)
};
for element in elts {
self.unpack(element, value_ty, scope);
@@ -121,6 +119,7 @@ impl<'db> Unpacker<'db> {
pub(crate) fn finish(mut self) -> UnpackResult<'db> {
self.targets.shrink_to_fit();
UnpackResult {
diagnostics: self.diagnostics.finish(),
targets: self.targets,
}
}
@@ -129,10 +128,15 @@ impl<'db> Unpacker<'db> {
#[derive(Debug, Default, PartialEq, Eq)]
pub(crate) struct UnpackResult<'db> {
targets: FxHashMap<ScopedExpressionId, Type<'db>>,
diagnostics: TypeCheckDiagnostics,
}
impl<'db> UnpackResult<'db> {
pub(crate) fn get(&self, expr_id: ScopedExpressionId) -> Option<Type<'db>> {
self.targets.get(&expr_id).copied()
}
pub(crate) fn diagnostics(&self) -> &TypeCheckDiagnostics {
&self.diagnostics
}
}

View File

@@ -1,7 +1,8 @@
use camino::Utf8Path;
use dir_test::{dir_test, Fixture};
use std::ffi::OsStr;
use std::path::Path;
use dir_test::{dir_test, Fixture};
/// See `crates/red_knot_test/README.md` for documentation on these tests.
#[dir_test(
dir: "$CARGO_MANIFEST_DIR/resources/mdtest",
@@ -9,46 +10,16 @@ use std::path::Path;
)]
#[allow(clippy::needless_pass_by_value)]
fn mdtest(fixture: Fixture<&str>) {
let fixture_path = Utf8Path::new(fixture.path());
let fixture_path = Path::new(fixture.path());
let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let workspace_root = crate_dir.ancestors().nth(2).unwrap();
let workspace_root = crate_dir.parent().and_then(Path::parent).unwrap();
let long_title = fixture_path.strip_prefix(workspace_root).unwrap();
let short_title = fixture_path.file_name().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 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
red_knot_test::run(fixture_path, long_title, short_title);
}

View File

@@ -91,11 +91,11 @@ fn background_request_task<'a, R: traits::BackgroundDocumentRequestHandler>(
let db = match path {
AnySystemPath::System(path) => {
match session.workspace_db_for_path(path.as_std_path()) {
Some(db) => db.clone(),
None => session.default_workspace_db().clone(),
Some(db) => db.snapshot(),
None => session.default_workspace_db().snapshot(),
}
}
AnySystemPath::SystemVirtual(_) => session.default_workspace_db().clone(),
AnySystemPath::SystemVirtual(_) => session.default_workspace_db().snapshot(),
};
let Some(snapshot) = session.take_snapshot(url) else {

View File

@@ -26,7 +26,7 @@ pub(crate) struct Requester<'s> {
response_handlers: FxHashMap<lsp_server::RequestId, ResponseBuilder<'s>>,
}
impl Client<'_> {
impl<'s> Client<'s> {
pub(super) fn new(sender: ClientSender) -> Self {
Self {
notifier: Notifier(sender.clone()),

View File

@@ -20,7 +20,6 @@ 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 }

View File

@@ -184,11 +184,8 @@ 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.
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.
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.
## Structured test suites

View File

@@ -7,7 +7,6 @@ use ruff_db::vendored::VendoredFileSystem;
use ruff_db::{Db as SourceDb, Upcast};
#[salsa::db]
#[derive(Clone)]
pub(crate) struct Db {
workspace_root: SystemPathBuf,
storage: salsa::Storage<Self>,

View File

@@ -1,12 +1,13 @@
use camino::Utf8Path;
use colored::Colorize;
use parser as test_parser;
use red_knot_python_semantic::types::check_types;
use ruff_db::diagnostic::{CompileDiagnostic, Diagnostic};
use ruff_db::diagnostic::{Diagnostic, ParseDiagnostic};
use ruff_db::files::{system_path_to_file, File, Files};
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;
@@ -14,30 +15,23 @@ 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: &Utf8Path, long_title: &str, short_title: &str, test_name: &str) {
pub fn run(path: &Path, long_title: &str, short_title: &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 `{path}`: {err}")
panic!("Error parsing `{}`: {err}", path.to_str().unwrap())
}
};
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);
@@ -60,15 +54,6 @@ pub fn run(path: &Utf8Path, long_title: &str, short_title: &str, test_name: &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()
);
}
}
@@ -101,9 +86,24 @@ fn run_test(db: &mut db::Db, test: &parser::MarkdownTest) -> Result<(), Failures
let failures: Failures = test_files
.into_iter()
.filter_map(|test_file| {
let mut diagnostics = check_types::accumulated::<CompileDiagnostic>(db, test_file.file);
// Filter out diagnostics that are not related to the current file.
diagnostics.retain(|diagnostic| diagnostic.file() == test_file.file);
let parsed = parsed_module(db, test_file.file);
let mut diagnostics: Vec<Box<_>> = parsed
.errors()
.iter()
.cloned()
.map(|error| {
let diagnostic: Box<dyn Diagnostic> =
Box::new(ParseDiagnostic::new(test_file.file, error));
diagnostic
})
.collect();
let type_diagnostics = check_types(db, test_file.file);
diagnostics.extend(type_diagnostics.into_iter().map(|diagnostic| {
let diagnostic: Box<dyn Diagnostic> = Box::new((*diagnostic).clone());
diagnostic
}));
match matcher::match_file(db, test_file.file, diagnostics) {
Ok(()) => None,

View File

@@ -1 +1 @@
0a2da01946a406ede42e9c66f416a7e7758991d6
5052fa2f18db4493892e0f2775030683c9d06531

View File

@@ -33,7 +33,6 @@ _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
@@ -41,7 +40,6 @@ _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-
@@ -54,7 +52,6 @@ _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-
@@ -142,12 +139,6 @@ 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-

View File

@@ -2,13 +2,10 @@ import codecs
import sys
from _typeshed import ReadableBuffer
from collections.abc import Callable
from typing import Literal, final, overload, type_check_only
from typing import Literal, overload
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: ...

View File

@@ -73,7 +73,6 @@ _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):
@@ -82,7 +81,6 @@ 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]: ...
@@ -90,7 +88,6 @@ 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):

View File

@@ -1,9 +1,9 @@
import csv
import sys
from _typeshed import SupportsWrite
from collections.abc import Iterable
from typing import Any, Final, type_check_only
from typing_extensions import Self, TypeAlias
from collections.abc import Iterable, Iterator
from typing import Any, Final
from typing_extensions import TypeAlias
__version__: Final[str]
@@ -45,47 +45,17 @@ class Dialect:
strict: bool = False,
) -> None: ...
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 _reader(Iterator[list[str]]):
@property
def dialect(self) -> Dialect: ...
line_num: int
def __next__(self) -> list[str]: ...
# 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: ...
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],

View File

@@ -1,10 +1,9 @@
import _typeshed
import sys
from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
from _typeshed import ReadableBuffer, WriteableBuffer
from abc import abstractmethod
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p
from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only
from typing import Any, ClassVar, Generic, TypeVar, overload
from typing_extensions import Self, TypeAlias
if sys.version_info >= (3, 9):
@@ -48,79 +47,46 @@ if sys.platform == "win32":
def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ...
def FreeLibrary(handle: int, /) -> None: ...
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 _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]
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:
class _CData(metaclass=_CDataMeta):
_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 __ctypes_from_outparam__(self, /) -> Self: ...
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
# 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):
class _SimpleCData(_CData, Generic[_T]):
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): ...
# 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):
class _Pointer(_PointerLike, _CData, Generic[_CT]):
_type_: type[_CT]
contents: _CT
@overload
@@ -139,32 +105,16 @@ 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 | _CDataType, offset: int = ...) -> _CArgObject: ...
def byref(obj: _CData, offset: int = ...) -> _CArgObject: ...
_ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType]
_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData]
_PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any]
# 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]]
class CFuncPtr(_PointerLike, _CData):
restype: type[_CData] | Callable[[int], Any] | None
argtypes: Sequence[type[_CData]]
errcheck: _ECT
# Abstract attribute that must be defined on subclasses
_flags_: ClassVar[int]
@@ -179,7 +129,7 @@ class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType):
if sys.platform == "win32":
@overload
def __init__(
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., /
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., /
) -> None: ...
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
@@ -187,95 +137,30 @@ class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType):
_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
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: ...
@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: ...
# 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.
class _StructUnionMeta(_CDataMeta):
_fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]]
_pack_: int
_anonymous_: Sequence[str]
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: ...
# 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 Union(_StructUnionBase): ...
class Structure(_StructUnionBase): ...
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):
class Array(_CData, Generic[_CT]):
@property
@abstractmethod
def _length_(self) -> int: ...
@@ -320,15 +205,9 @@ class Array(_CData, Generic[_CT], metaclass=_PyCArrayType):
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
def addressof(obj: _CData | _CDataType, /) -> int: ...
def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...
def addressof(obj: _CData, /) -> int: ...
def alignment(obj_or_type: _CData | type[_CData], /) -> int: ...
def get_errno() -> int: ...
def resize(obj: _CData | _CDataType, size: int, /) -> None: ...
def resize(obj: _CData, size: int, /) -> None: ...
def set_errno(value: int, /) -> 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: ...
def sizeof(obj_or_type: _CData | type[_CData], /) -> int: ...

View File

@@ -1,7 +1,6 @@
import sys
from _typeshed import ReadOnlyBuffer, SupportsRead
from curses import _ncurses_version
from typing import IO, Any, final, overload
from typing import IO, Any, NamedTuple, final, overload
from typing_extensions import TypeAlias
# NOTE: This module is ordinarily only available on Unix, but the windows-curses
@@ -550,4 +549,9 @@ 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

View File

@@ -1,27 +0,0 @@
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: ...

View File

@@ -1,7 +1,7 @@
import sys
from _typeshed import ReadOnlyBuffer, StrOrBytesPath
from types import TracebackType
from typing import TypeVar, final, overload, type_check_only
from typing import TypeVar, overload
from typing_extensions import Self, TypeAlias
if sys.platform != "win32":
@@ -13,8 +13,6 @@ 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):
@@ -24,17 +22,18 @@ 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: ...
# This isn't true, but the class can't be instantiated. See #13024
def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ...
# Don't exist at runtime
__new__: None # type: ignore[assignment]
__init__: None # type: ignore[assignment]

View File

@@ -17,13 +17,20 @@ 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]

View File

@@ -1,80 +0,0 @@
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: ...

View File

@@ -45,6 +45,5 @@ 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]: ...

View File

@@ -1,44 +0,0 @@
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: ...

View File

@@ -4,6 +4,7 @@ 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],

View File

@@ -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 CapsuleType, TypeAlias
from typing_extensions import TypeAlias
_CMSG: TypeAlias = tuple[int, int, bytes]
_CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer]
@@ -72,8 +72,7 @@ SO_SNDBUF: int
SO_SNDLOWAT: int
SO_SNDTIMEO: int
SO_TYPE: int
if sys.platform != "linux":
SO_USELOOPBACK: int
SO_USELOOPBACK: int
if sys.platform == "win32":
SO_EXCLUSIVEADDRUSE: int
if sys.platform != "win32":
@@ -88,10 +87,7 @@ 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
@@ -103,32 +99,27 @@ 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_ERRQUEUE: int
if sys.platform == "win32":
MSG_BCAST: int
MSG_ERRQUEUE: 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
@@ -137,11 +128,10 @@ 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
@@ -153,22 +143,21 @@ 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 != "linux":
IPPROTO_GGP: int
IPPROTO_IPV4: int
IPPROTO_MAX: int
IPPROTO_ND: int
if sys.platform == "win32":
if sys.platform != "darwin":
IPPROTO_CBT: int
IPPROTO_ICLFXBM: int
IPPROTO_IGP: int
@@ -177,19 +166,18 @@ if sys.platform == "win32":
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" and sys.platform != "linux":
IPPROTO_BIP: int # Not FreeBSD either
IPPROTO_MOBILE: int # Not FreeBSD either
IPPROTO_VRRP: int # Not FreeBSD either
if sys.platform != "win32" and sys.platform != "darwin":
IPPROTO_BIP: int
IPPROTO_MOBILE: int
IPPROTO_VRRP: int
if sys.version_info >= (3, 9) and sys.platform == "linux":
# Availability: Linux >= 2.6.20, FreeBSD >= 10.1
IPPROTO_UDPLITE: int
@@ -214,10 +202,11 @@ IP_MULTICAST_IF: int
IP_MULTICAST_LOOP: int
IP_MULTICAST_TTL: int
IP_OPTIONS: int
if sys.platform != "linux":
IP_RECVDSTADDR: int
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":
@@ -229,7 +218,6 @@ 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
@@ -267,9 +255,6 @@ 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
@@ -283,12 +268,11 @@ 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
@@ -297,7 +281,7 @@ AI_NUMERICHOST: int
AI_NUMERICSERV: int
AI_PASSIVE: int
AI_V4MAPPED: int
if sys.platform != "win32" and sys.platform != "linux":
if sys.platform != "win32":
AI_DEFAULT: int
AI_MASK: int
AI_V4MAPPED_CFG: int
@@ -309,8 +293,6 @@ 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
@@ -336,27 +318,6 @@ 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
@@ -373,13 +334,12 @@ 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
@@ -477,13 +437,12 @@ 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
@@ -497,10 +456,6 @@ 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
@@ -567,17 +522,16 @@ 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
if sys.platform != "linux":
AF_LINK: int
AF_LINK: int
has_ipv6: bool
if sys.platform != "darwin" and sys.platform != "linux":
if sys.platform != "darwin":
if sys.platform != "win32" or sys.version_info >= (3, 9):
BDADDR_ANY: str
BDADDR_LOCAL: str
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
if sys.platform != "win32" and sys.platform != "darwin":
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
@@ -626,37 +580,36 @@ 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" and sys.platform != "linux":
if sys.platform != "darwin":
if sys.version_info >= (3, 9) or sys.platform != "win32":
AF_BLUETOOTH: int
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
if sys.platform != "win32" and sys.platform != "darwin":
# 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" and sys.platform != "linux":
if sys.platform != "darwin":
if sys.version_info >= (3, 9) or sys.platform != "win32":
BTPROTO_RFCOMM: int
@@ -683,14 +636,13 @@ 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
@@ -709,12 +661,10 @@ if sys.platform != "win32" and sys.platform != "darwin":
# Miscellaneous undocumented
if sys.platform != "win32" and sys.platform != "linux":
if sys.platform != "win32":
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 =====
@@ -842,4 +792,4 @@ def if_nameindex() -> list[tuple[int, str]]: ...
def if_nametoindex(oname: str, /) -> int: ...
def if_indextoname(index: int, /) -> str: ...
CAPI: CapsuleType
CAPI: object

View File

@@ -44,12 +44,6 @@ 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: ...
@@ -57,9 +51,7 @@ 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: ...

View File

@@ -1,4 +1,3 @@
from threading import RLock
from typing import Any
from typing_extensions import Self, TypeAlias
from weakref import ReferenceType
@@ -9,9 +8,6 @@ _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: ...

View File

@@ -107,7 +107,7 @@ class object:
@property
def __class__(self) -> type[Self]: ...
@__class__.setter
def __class__(self, type: type[Self], /) -> None: ...
def __class__(self, type: type[object], /) -> None: ...
def __init__(self) -> None: ...
def __new__(cls) -> Self: ...
# N.B. `object.__setattr__` and `object.__delattr__` are heavily special-cased by type checkers.
@@ -1963,33 +1963,14 @@ class StopAsyncIteration(Exception):
class SyntaxError(Exception):
msg: str
filename: str | None
lineno: int | None
offset: int | None
text: str | None
# Errors are displayed differently if this attribute exists on the exception.
# The value is always None.
print_file_and_line: None
filename: str | 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): ...

View File

@@ -126,7 +126,6 @@ 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: ...

View File

@@ -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, ClassVar, Final, Literal, Protocol, TextIO
from typing import Any, BinaryIO, Final, Literal, Protocol, TextIO
from typing_extensions import Self
__all__ = [
@@ -202,7 +202,6 @@ 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: ...
@@ -210,14 +209,11 @@ 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: ...

View File

@@ -1,5 +1,5 @@
import sys
from _typeshed import MaybeNone, StrOrBytesPath, SupportsWrite
from _typeshed import 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 | MaybeNone: ...
def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | Any: ...
@overload
def get(
self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T
) -> str | _T | MaybeNone: ...
) -> str | _T | Any: ...
@overload
def items(self, *, raw: bool = False, vars: _Section | None = None) -> ItemsView[str, SectionProxy]: ...
@overload
@@ -277,8 +277,6 @@ 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
@@ -302,34 +300,28 @@ class SectionProxy(MutableMapping[str, str]):
def parser(self) -> RawConfigParser: ...
@property
def name(self) -> str: ...
# 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(
def get( # type: ignore[override]
self,
option: str,
fallback: _T,
fallback: str | None = None,
*,
raw: bool = False,
vars: _Section | None = None,
_impl: Any | None = None,
**kwargs: Any,
) -> str | _T: ...
) -> str | Any: ... # can be None in RawConfigParser's sections
# 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 | None: ...
def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int: ...
@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 | None: ...
def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float: ...
@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 | None: ...
def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool: ...
@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

View File

@@ -1,7 +1,7 @@
import abc
import sys
from _typeshed import FileDescriptorOrPath, Unused
from abc import ABC, abstractmethod
from abc import 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,22 +38,16 @@ _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(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
class AbstractContextManager(Protocol[_T_co, _ExitT_co]):
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(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]):
async def __aenter__(self) -> _T_co: ...
@abstractmethod
async def __aexit__(

View File

@@ -1,15 +1,8 @@
import sys
from typing import Final, NamedTuple, type_check_only
from typing import Final
if sys.platform != "win32":
@type_check_only
class _MethodBase(NamedTuple):
name: str
ident: str | None
salt_chars: int
total_size: int
class _Method(_MethodBase): ...
class _Method: ...
METHOD_CRYPT: Final[_Method]
METHOD_MD5: Final[_Method]
METHOD_SHA256: Final[_Method]

View File

@@ -8,6 +8,8 @@ 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,
@@ -19,10 +21,6 @@ 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
@@ -79,7 +77,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
@@ -127,7 +125,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],

View File

@@ -10,11 +10,13 @@ from _ctypes import (
_CanCastTo as _CanCastTo,
_CArgObject as _CArgObject,
_CData as _CData,
_CDataType as _CDataType,
_CDataMeta as _CDataMeta,
_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,
@@ -26,7 +28,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 Self, TypeAlias, deprecated
from typing_extensions import TypeAlias
if sys.platform == "win32":
from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error
@@ -39,7 +41,6 @@ if sys.version_info >= (3, 9):
_T = TypeVar("_T")
_DLLT = TypeVar("_DLLT", bound=CDLL)
_CT = TypeVar("_CT", bound=_CData)
DEFAULT_MODE: int
@@ -47,7 +48,7 @@ class ArgumentError(Exception): ...
class CDLL:
_func_flags_: ClassVar[int]
_func_restype_: ClassVar[_CDataType]
_func_restype_: ClassVar[_CData]
_name: str
_handle: int
_FuncPtr: type[_FuncPointer]
@@ -90,21 +91,15 @@ class _NamedFuncPointer(_FuncPointer):
__name__: str
def CFUNCTYPE(
restype: type[_CData | _CDataType] | None,
*argtypes: type[_CData | _CDataType],
use_errno: bool = ...,
use_last_error: bool = ...,
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
) -> type[_FuncPointer]: ...
if sys.platform == "win32":
def WINFUNCTYPE(
restype: type[_CData | _CDataType] | None,
*argtypes: type[_CData | _CDataType],
use_errno: bool = ...,
use_last_error: bool = ...,
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
) -> type[_FuncPointer]: ...
def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ...
def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> 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.)
@@ -117,17 +112,12 @@ _CVoidConstPLike: TypeAlias = _CVoidPLike | bytes
_CastT = TypeVar("_CastT", bound=_CanCastTo)
def cast(obj: _CData | _CDataType | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...
def cast(obj: _CData | _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: ...
@@ -136,12 +126,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(ptr: _CVoidConstPLike, size: int = -1) -> bytes: ...
def string_at(address: _CVoidConstPLike, size: int = -1) -> bytes: ...
if sys.platform == "win32":
def WinError(code: int | None = None, descr: str | None = None) -> OSError: ...
def wstring_at(ptr: _CVoidConstPLike, size: int = -1) -> str: ...
def wstring_at(address: _CVoidConstPLike, size: int = -1) -> str: ...
class c_byte(_SimpleCData[int]): ...
@@ -150,8 +140,6 @@ 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
@@ -167,13 +155,7 @@ 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]):
@classmethod
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
c_voidp = c_void_p # backwards compatibility (to a bug)
class c_void_p(_PointerLike, _SimpleCData[int | None]): ...
class c_wchar(_SimpleCData[str]): ...
c_int8 = c_byte
@@ -192,8 +174,6 @@ 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: ...

View File

@@ -1 +0,0 @@
__version__: str

View File

@@ -1,8 +0,0 @@
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: ...

View File

@@ -1,14 +0,0 @@
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: ...

View File

@@ -1,14 +0,0 @@
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: ...

View File

@@ -4,5 +4,3 @@ def find_library(name: str) -> str | None: ...
if sys.platform == "win32":
def find_msvcrt() -> str | None: ...
def test() -> None: ...

View File

@@ -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 Any, TypeVar
from typing_extensions import Self, TypeAlias
from typing import TypeVar
from typing_extensions import TypeAlias
BYTE = c_byte
WORD = c_ushort
@@ -241,16 +241,10 @@ LPBYTE = PBYTE
PBOOLEAN = PBYTE
# LP_c_char
class PCHAR(_Pointer[CHAR]):
# this is inherited from ctypes.c_char_p, kind of.
@classmethod
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
class PCHAR(_Pointer[CHAR]): ...
# LP_c_wchar
class PWCHAR(_Pointer[WCHAR]):
# inherited from ctypes.c_wchar_p, kind of
@classmethod
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
class PWCHAR(_Pointer[WCHAR]): ...
# LP_c_void_p
class PHANDLE(_Pointer[HANDLE]): ...

View File

@@ -1,9 +1,7 @@
import sys
from _curses import *
from _curses import window as window
from _typeshed import structseq
from collections.abc import Callable
from typing import Final, TypeVar, final, type_check_only
from typing import TypeVar
from typing_extensions import Concatenate, ParamSpec
# NOTE: The _curses module is ordinarily only available on Unix, but the
@@ -27,19 +25,3 @@ 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: ...

View File

@@ -1 +1,22 @@
from _curses_panel import *
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: ...

View File

@@ -1,8 +1,8 @@
import sys
from abc import abstractmethod
from time import struct_time
from typing import ClassVar, Final, NoReturn, SupportsIndex, final, overload, type_check_only
from typing_extensions import CapsuleType, Self, TypeAlias, deprecated
from typing import ClassVar, Final, NamedTuple, NoReturn, SupportsIndex, final, overload
from typing_extensions import Self, TypeAlias, deprecated
if sys.version_info >= (3, 11):
__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC")
@@ -40,17 +40,10 @@ if sys.version_info >= (3, 11):
UTC: timezone
if sys.version_info >= (3, 9):
# 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 _IsoCalendarDate(NamedTuple):
year: int
week: int
weekday: int
class date:
min: ClassVar[date]
@@ -332,5 +325,3 @@ class datetime(date):
def __sub__(self, value: Self, /) -> timedelta: ...
@overload
def __sub__(self, value: timedelta, /) -> Self: ...
datetime_CAPI: CapsuleType

View File

@@ -2,7 +2,7 @@ import sys
from _typeshed import StrOrBytesPath
from collections.abc import Iterator, MutableMapping
from types import TracebackType
from typing import Literal, type_check_only
from typing import Literal
from typing_extensions import Self, TypeAlias
__all__ = ["open", "whichdb", "error"]
@@ -89,8 +89,6 @@ 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]]

View File

@@ -24,8 +24,7 @@ from _decimal import (
setcontext as setcontext,
)
from collections.abc import Container, Sequence
from types import TracebackType
from typing import Any, ClassVar, Literal, NamedTuple, final, overload, type_check_only
from typing import Any, ClassVar, Literal, NamedTuple, overload
from typing_extensions import Self, TypeAlias
_Decimal: TypeAlias = Decimal | int
@@ -36,14 +35,6 @@ _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, ...]

View File

@@ -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: Iterable[str]) -> None: ...
def spawn(self, cmd: list[str]) -> None: ...
def mkpath(self, name: str, mode: int = 0o777) -> None: ...
@overload
def move_file(self, src: StrPath, dst: _StrPathT) -> _StrPathT | str: ...

View File

@@ -1,10 +1,6 @@
from collections.abc import Iterable
from typing import Literal
def spawn(
cmd: Iterable[str],
search_path: bool | Literal[0, 1] = 1,
verbose: bool | Literal[0, 1] = 0,
dry_run: bool | Literal[0, 1] = 0,
cmd: list[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: ...

View File

@@ -3,7 +3,7 @@ import types
import unittest
from _typeshed import ExcInfo
from collections.abc import Callable
from typing import Any, NamedTuple, type_check_only
from typing import Any, ClassVar, NamedTuple
from typing_extensions import Self, TypeAlias
__all__ = [
@@ -42,15 +42,17 @@ __all__ = [
"debug",
]
# MyPy errors on conditionals within named tuples.
if sys.version_info >= (3, 13):
@type_check_only
class _TestResultsBase(NamedTuple):
class TestResults(NamedTuple):
def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... # type: ignore[misc]
skipped: int
failed: int
attempted: int
class TestResults(_TestResultsBase):
def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ...
skipped: int
_fields: ClassVar = ("failed", "attempted") # type: ignore[misc]
__match_args__ = ("failed", "attempted") # type: ignore[misc]
__doc__: None # type: ignore[misc]
else:
class TestResults(NamedTuple):

View File

@@ -4,29 +4,6 @@ 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
@@ -35,3 +12,18 @@ 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

View File

@@ -5,8 +5,6 @@ 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

View File

@@ -1 +0,0 @@
aliases: dict[str, str]

View File

@@ -1,30 +0,0 @@
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: ...

View File

@@ -1,26 +0,0 @@
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: ...

View File

@@ -1,23 +0,0 @@
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: ...

View File

@@ -1,23 +0,0 @@
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: ...

View File

@@ -1,26 +0,0 @@
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: ...

View File

@@ -1,33 +0,0 @@
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: ...

View File

@@ -1,21 +0,0 @@
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

View File

@@ -1,21 +0,0 @@
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

View File

@@ -1,21 +0,0 @@
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