Compare commits

..

160 Commits

Author SHA1 Message Date
Dhruv Manilawala
2d0468137c Extract creating workspaces in the server 2024-10-16 14:51:23 +05:30
Dhruv Manilawala
c6b311c546 Update setup image for PyCharm External Tool (#13767)
## Summary

fixes: #13765 

## Preview

<img width="624" alt="Screenshot 2024-10-16 at 10 05 57"
src="https://github.com/user-attachments/assets/c0eccda5-3cf1-4119-a9b5-d86b01a8c64c">
2024-10-16 04:41:37 +00:00
Dhruv Manilawala
b16f665a81 [red-knot] Infer target types for unpacked tuple assignment (#13316)
## Summary

This PR adds support for unpacking tuple expression in an assignment
statement where the target expression can be a tuple or a list (the
allowed sequence targets).

The implementation introduces a new `infer_assignment_target` which can
then be used for other targets like the ones in for loops as well. This
delegates it to the `infer_definition`. The final implementation uses a
recursive function that visits the target expression in source order and
compares the variable node that corresponds to the definition. At the
same time, it keeps track of where it is on the assignment value type.

The logic also accounts for the number of elements on both sides such
that it matches even if there's a gap in between. For example, if
there's a starred expression like `(a, *b, c) = (1, 2, 3)`, then the
type of `a` will be `Literal[1]` and the type of `b` will be
`Literal[2]`.

There are a couple of follow-ups that can be done:
* Use this logic for other target positions like `for` loop
* Add diagnostics for mis-match length between LHS and RHS

## Test Plan

Add various test cases using the new markdown test framework.
Validate that existing test cases pass.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-15 19:07:11 +00:00
Alex
d77480768d [red-knot] Port type inference tests to new test framework (#13719)
## Summary

Porting infer tests to new markdown tests framework.

Link to the corresponding issue: #13696

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-10-15 11:23:46 -07:00
github-actions[bot]
5fa82fb0cd Sync vendored typeshed stubs (#13753) 2024-10-15 13:36:11 +00:00
David Peter
74bf4b0653 [red knot] Fix narrowing for '… is not …' type guards, add '… is …' type guards (#13758)
## Summary

- Fix a bug with `… is not …` type guards.
 
  Previously, in an example like
  ```py
  x = [1]
  y = [1]
  
  if x is not y:
      reveal_type(x)
  ```
  we would infer a type of `list[int] & ~list[int] == Never` for `x`
  inside the conditional (instead of `list[int]`), since we built a
  (negative) intersection with the type of the right hand side (`y`).
  However, as this example shows, this assumption can only be made for
  singleton types (types with a single inhabitant) such as `None`.
- Add support for `… is …` type guards.

closes #13715

## Test Plan

Moved existing `narrow_…` tests to Markdown-based tests and added new
ones (including a regression test for the bug described above). Note
that will create some conflicts with
https://github.com/astral-sh/ruff/pull/13719. I tried to establish the
correct organizational structure as proposed in
https://github.com/astral-sh/ruff/pull/13719#discussion_r1800188105
2024-10-15 14:49:32 +02:00
Micha Reiser
5f65e842e8 Upgrade salsa (#13757) 2024-10-15 11:06:32 +00:00
Micha Reiser
72ac6cd5a5 Fix TODO directive out of bounds acccess (#13756) 2024-10-15 10:49:53 +02:00
David Peter
04b636cba2 [red knot] Use memmem::find instead of custom version (#13750)
This is a follow-up on #13746:

- Use `memmem::find` instead of rolling our own inferior version.
- Avoid `x.as_ref()` calls using `&**x`
2024-10-14 15:17:19 +02:00
Alex Waygood
6048f331d9 [red-knot] Add a build.rs file to red_knot_python_semantic, and document pitfalls of using rstest in combination with mdtest (#13747) 2024-10-14 13:02:03 +01:00
David Peter
93097f1c53 [red-knot] feat: Inference for BytesLiteral comparisons (#13746)
Implements inference for `BytesLiteral` comparisons along the lines of
https://github.com/astral-sh/ruff/pull/13634.

closes #13687

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-14 14:01:23 +02:00
Sid
9bb4722ebf [flake8-todos] Allow words starting with todo (#13640)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-14 10:21:45 +00:00
Dhruv Manilawala
5caabe54b6 Allow ipytest cell magic (#13745)
## Summary

fixes: #13718 

## Test Plan

Using the notebook as mentioned in
https://github.com/astral-sh/ruff/issues/13718#issuecomment-2410631674,
this PR does not give the "F821 Undefined name `test_sorted`"
diagnostic.
2024-10-14 15:48:33 +05:30
renovate[bot]
814ab47582 Update dependency @miniflare/storage-memory to v2.14.4 (#13737)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:52:28 +00:00
renovate[bot]
c3a3622e30 Update Rust crate libcst to v1.5.0 (#13739)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 09:51:13 +02:00
renovate[bot]
4ef422d3b4 Update Rust crate clap to v4.5.20 (#13733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 09:44:55 +02:00
renovate[bot]
58bc981677 Update Rust crate pathdiff to v0.2.2 (#13734)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 09:41:33 +02:00
renovate[bot]
dd5018ac55 Update dependency @miniflare/kv to v2.14.4 (#13736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:40:49 +00:00
renovate[bot]
63df94b521 Update Rust crate proc-macro2 to v1.0.87 (#13735)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:39:46 +00:00
renovate[bot]
e4c0dd6f96 Update rust-wasm-bindgen monorepo (#13738)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 07:38:45 +00:00
Micha Reiser
3111dce5b4 Fix mkdocs CI job (#13744) 2024-10-14 09:31:35 +02:00
Micha Reiser
8445e4725c Downgrade benchmarks CI job to ubuntu 22 (#13743) 2024-10-14 09:17:38 +02:00
Alex Waygood
defdc4dd8e [red-knot] Use colors to improve readability of mdtest output (#13725) 2024-10-13 14:20:35 +01:00
Steve C
46bc69d1d4 [flake8-pyi] - fix dropped exprs in PYI030 autofix (#13727) 2024-10-13 11:33:03 +01:00
Carl Meyer
3209953276 [red-knot] clarify mdtest README (#13720)
Address a potential point of confusion that bit a contributor in
https://github.com/astral-sh/ruff/pull/13719

Also remove a no-longer-accurate line about bare `error: ` assertions
(which are no longer allowed) and clarify another point about which
kinds of error assertions to use.
2024-10-11 12:36:48 -07:00
Carl Meyer
6ae833e0c7 [red-knot] mdtest usability improvements for reveal_type (#13709)
## Summary

Fixes #13708.

Silence `undefined-reveal` diagnostic on any line including a `#
revealed:` assertion.

Add more context to un-silenced `undefined-reveal` diagnostics in mdtest
test failures. This doesn't make the failure output less verbose, but it
hopefully clarifies the right fix for an `undefined-reveal` in mdtest,
while still making it clear what red-knot's normal diagnostic for this
looks like.

## Test Plan

Added and updated tests.
2024-10-10 17:33:53 -07:00
Carl Meyer
a3dc5c0529 [red-knot] document test framework (#13695)
This adds documentation for the new test framework.

I also added documentation for the planned design of features we haven't
built yet (clearly marked as such), so that this doc can become the sole
source of truth for the test framework design (we don't need to refer
back to the original internal design document.)

Also fixes a few issues in the test framework implementation that were
discovered in writing up the docs.

---------

Co-authored-by: T-256 <132141463+T-256@users.noreply.github.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-10-10 12:02:01 -07:00
Alex Waygood
d6b24b690a [pycodestyle] Fix whitespace-related false positives and false negatives inside type-parameter lists (#13704) 2024-10-10 17:24:17 +01:00
Alex Waygood
5b4afd30ca Harmonise methods for distinguishing different Python source types (#13682) 2024-10-09 13:18:52 +00:00
Micha Reiser
b9827a4122 Remove layout values from AnyStringPart (#13681) 2024-10-09 07:25:40 +01:00
Carl Meyer
93eff7f174 [red-knot] type inference/checking test framework (#13636)
## Summary

Adds a markdown-based test framework for writing tests of type inference
and type checking. Fixes #11664.

Implements the basic required features. A markdown test file is a suite
of tests, each test can contain one or more Python files, with
optionally specified path/name. The test writes all files to an
in-memory file system, runs red-knot, and matches the resulting
diagnostics against `Type: ` and `Error: ` assertions embedded in the
Python source as comments.

We will want to add features like incremental tests, setting custom
configuration for tests, writing non-Python files, testing syntax
errors, capturing full diagnostic output, etc. There's also plenty of
room for improved UX (colored output?).

## Test Plan

Lots of tests!

Sample of the current output when a test fails:

```
     Running tests/inference.rs (target/debug/deps/inference-7c96590aa84de2a4)

running 1 test
test inference::path_1_resources_inference_numbers_md ... FAILED

failures:

---- inference::path_1_resources_inference_numbers_md stdout ----
inference/numbers.md - Numbers - Floats
  /src/test.py
    line 2: unexpected error: [invalid-assignment] "Object of type `Literal["str"]` is not assignable to `int`"

thread 'inference::path_1_resources_inference_numbers_md' panicked at crates/red_knot_test/src/lib.rs:60:5:
Some tests failed.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    inference::path_1_resources_inference_numbers_md

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s

error: test failed, to rerun pass `-p red_knot_test --test inference`
```

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-08 12:33:19 -07:00
Micha Reiser
fc661e193a Normalize implicit concatenated f-string quotes per part (#13539) 2024-10-08 09:59:17 +00:00
Zanie Blue
42fcbef876 Fix typo in allow-unused-imports documentation (#13669) 2024-10-07 14:08:36 -05:00
Alex Waygood
71b52b83e4 [red-knot] Allow type[] to be subscripted (#13667)
Fixed a TODO by adding another TODO. It's the red-knot way!

## Summary

`builtins.type` can be subscripted at runtime on Python 3.9+, even
though it has no `__class_getitem__` method and its metaclass (which
is... itself) has no `__getitem__` method. The special case is
[hardcoded directly into `PyObject_GetItem` in
CPython](744caa8ef4/Objects/abstract.c (L181-L184)).
We just have to replicate the special case in our semantic model.

This will fail at runtime on Python <3.9. However, there's a bunch of
outstanding questions (detailed in the TODO comment I added) regarding
how we deal with subscriptions of other generic types on lower Python
versions. Since we want to avoid too many false positives for now, I
haven't tried to address this; I've just made `type` subscriptable on
all Python versions.

## Test Plan

`cargo test -p red_knot_python_semantic --lib`
2024-10-07 19:43:47 +01:00
Zanie Blue
fb90f5a13d Add known limitation to C416 with dictionaries (#13627)
Part of https://github.com/astral-sh/ruff/issues/13625

See also #13629
2024-10-07 16:20:45 +00:00
Alex Waygood
d7484e6942 [red-knot] Improve type inference for except handlers where a tuple of exception classes is caught (#13646) 2024-10-07 16:13:06 +01:00
Dylan
14ee5dbfde [refurb] Count codepoints not bytes for slice-to-remove-prefix-or-suffix (FURB188) (#13631) 2024-10-07 16:13:28 +02:00
Alex Waygood
27ac34d683 Rework S606 (start-process-with-no-shell) docs to make clear the security motivations (#13658)
Helps with #13614. This docs rewrite draws on the [documentation for the
original bandit
rule](https://bandit.readthedocs.io/en/latest/plugins/b606_start_process_with_no_shell.html).
2024-10-07 13:31:01 +01:00
Sid
31ca1c3064 [flake8-async] allow async generators (ASYNC100) (#13639)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Treat async generators as "await" in ASYNC100.

Fixes #13637

## Test Plan

Updated snapshot
2024-10-07 07:25:54 -05:00
qdegraaf
646e4136d7 [flake8-bugbear] Tweak B905 message to not suggest setting parameter strict= to False (#13656)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-07 11:56:17 +00:00
Alex Waygood
58a11b33da Fixup docs markup for RUF027 (#13659) 2024-10-07 11:49:45 +00:00
renovate[bot]
7856e90a2c Update pre-commit dependencies (#13650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 10:16:58 +01:00
renovate[bot]
98878c9bf2 Update dependency tomli to v2.0.2 (#13649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 10:16:43 +01:00
Aleksei Latyshev
73aa6ea417 [refurb] implement hardcoded-string-charset (FURB156) (#13530)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-07 07:35:14 +00:00
renovate[bot]
38d872ea4c Update Rust crate hashbrown to 0.15.0 (#13652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-10-07 08:50:59 +02:00
renovate[bot]
824def2194 Update dependency ruff to v0.6.9 (#13648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:15:58 +02:00
renovate[bot]
2ab78dd6a5 Update NPM Development dependencies (#13651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:15:26 +02:00
renovate[bot]
03fa7f64dd Update Rust crate clap to v4.5.19 (#13647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:05:40 +02:00
renovate[bot]
43330225be Update Rust crate serde_with to v3.11.0 (#13655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:04:04 +02:00
renovate[bot]
383d9d9f6e Update Rust crate once_cell to v1.20.2 (#13653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 08:03:12 +02:00
Simon
8108f83810 [red-knot] feat: add StringLiteral and LiteralString comparison (#13634)
## Summary

Implements string literal comparisons and fallbacks to `str` instance
for `LiteralString`.
Completes an item in #13618

## Test Plan

- Adds a dedicated test with non exhaustive cases

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-05 12:22:30 -07:00
Simon
f1205177fd [red-knot] fix: when simplifying union, True & False -> instance(bool) (#13644) 2024-10-05 19:01:10 +01:00
Simon
1c2cafc101 [red-knot] more ergonomic and efficient handling of known builtin classes (#13615) 2024-10-05 18:03:46 +01:00
Alex Waygood
7c5a7d909c [red-knot] Improve tests relating to type inference for exception handlers (#13643) 2024-10-05 16:59:36 +00:00
Zanie Blue
2a365bb278 Mark PLE1141 fix as unsafe (#13629)
Closes https://github.com/astral-sh/ruff/issues/13343

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-04 14:22:26 -05:00
Zanie Blue
020f4d4a54 Add test cases for RUF006 with lambdas (#13628)
As discussed in https://github.com/astral-sh/ruff/issues/13619
2024-10-04 14:09:43 -05:00
Simon
888930b7d3 [red-knot] feat: implement integer comparison (#13571)
## Summary

Implements the comparison operator for `[Type::IntLiteral]` and
`[Type::BooleanLiteral]` (as an artifact of special handling of `True` and
`False` in python).
Sets the framework to implement more comparison for types known at
static time (e.g. `BooleanLiteral`, `StringLiteral`), allowing us to only
implement cases of the triplet `<left> Type`, `<right> Type`, `CmpOp`.
Contributes to #12701 (without checking off an item yet).

## Test Plan

- Added a test for the comparison of literals that should include most
cases of note.
- Added a test for the comparison of int instances

Please note that the cases do not cover 100% of the branches as there
are many and the current testing strategy with variables make this
fairly confusing once we have too many in one test.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-04 10:40:59 -07:00
Zanie Blue
d726f09cf0 Fix PTH123 false positive when open is passed a file descriptor (#13616)
Closes https://github.com/astral-sh/ruff/issues/12871

Includes some minor semantic type inference extensions changes to help
with reliably detecting integers
2024-10-04 08:48:47 -05:00
Dhruv Manilawala
975be9c1c6 Bump version to 0.6.9 (#13624) 2024-10-04 18:51:13 +05:30
Zanie Blue
99e4566fce Mark FURB118 fix as unsafe (#13613)
Closes https://github.com/astral-sh/ruff/issues/13421
2024-10-03 21:39:22 +00:00
Simon Høxbro Hansen
7ad07c2c5d Add allow-unused-imports setting for unused-import rule (F401) (#13601)
## Summary
Resolves https://github.com/astral-sh/ruff/issues/9962 by allowing a
configuration setting `allowed-unused-imports`

TODO:
- [x] Figure out the correct name and place for the setting; currently,
I have added it top level.
- [x] The comparison is pretty naive. I tried using `glob::Pattern` but
couldn't get it to work in the configuration.
- [x] Add tests
- [x] Update documentations

## Test Plan

`cargo test`
2024-10-03 19:44:44 +00:00
Bernát Gábor
4aefe52393 Support ruff discovery in pip build environments (#13591)
Resolves https://github.com/astral-sh/ruff/issues/13321.

Contents of overlay:
```bash
/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin:
total 26M
-rwxr-xr-x 1 bgabor8 staff 26M Oct  1 08:22 ruff
drwxr-xr-x 3 bgabor8 staff  96 Oct  1 08:22 .
drwxr-xr-x 4 bgabor8 staff 128 Oct  1 08:22 ..
```

Python executable:
```bash
'/Users/bgabor8/git/github/ruff-find-bin-during-build/.venv/bin/python'
```
PATH is:
```bash
['/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin',
 '/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/normal/bin',
'/Library/Frameworks/Python.framework/Versions/3.11/bin',
'/Library/Frameworks/Python.framework/Versions/3.12/bin',
```
Not sure where to add tests, there does not seem to be any existing one.
Can someone help me with that?
2024-10-03 17:38:07 +00:00
Zanie Blue
cc1f766622 Preserve trivia (i.e. comments) in PLR5501 (#13573)
Closes https://github.com/astral-sh/ruff/issues/13545

As described in the issue, we move comments before the inner `if`
statement to before the newly constructed `elif` statement (previously
`else`).
2024-10-03 10:22:20 -05:00
Bernát Gábor
fdd0a22c03 Move to maintained mirror of prettier (#13592)
https://github.com/pre-commit/mirrors-prettier has been archived and is
no longer maintained.

Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
2024-10-03 15:35:27 +01:00
cake-monotone
3728d5b3a2 [pyupgrade] Fix UP043 to apply to collections.abc.Generator and collections.abc.AsyncGenerator (#13611)
## Summary

fix #13602 

Currently, `UP043` only applies to typing.Generator, but it should also
support collections.abc.Generator.

This update ensures `UP043` correctly handles both
`collections.abc.Generator` and `collections.abc.AsyncGenerator`

### UP043
> `UP043`
> Python 3.13 introduced the ability for type parameters to specify
default values. As such, the default type arguments for some types in
the standard library (e.g., Generator, AsyncGenerator) are now optional.
> Omitting type parameters that match the default values can make the
code more concise and easier to read.

```py
Generator[int, None, None] -> Generator[int]
```
2024-10-03 13:06:15 +01:00
Dhruv Manilawala
7e3894f5b3 Avoid short circuiting B017 for multiple context managers (#13609)
## Summary

fixes: #13603
2024-10-03 15:35:05 +05:30
Charlie Marsh
c3b40da0d2 Use backticks for code in red-knot messages (#13599)
## Summary

...and remove periods from messages that don't span more than a single
sentence.

This is more consistent with how we present user-facing messages in uv
(which has a defined style guide).
2024-10-02 03:14:28 +00:00
Charlie Marsh
ef45185dbc Allow users to provide custom diagnostic messages when unwrapping calls (#13597)
## Summary

You can now call `return_ty_result` to operate on a `Result` directly
thereby using your own diagnostics, as in:

```rust
return dunder_getitem_method
    .call(self.db, &[slice_ty])
    .return_ty_result(self.db, value.as_ref().into(), self)
    .unwrap_or_else(|err| {
        self.add_diagnostic(
            (&**value).into(),
            "call-non-callable",
            format_args!(
                "Method `__getitem__` is not callable on object of type '{}'.",
                value_ty.display(self.db),
            ),
        );
        err.return_ty()
    });
```
2024-10-01 21:22:13 +00:00
Charlie Marsh
961fc98344 Use __class_getitem__ for more specific non-subscript errors (#13596) 2024-10-01 18:16:00 +00:00
Charlie Marsh
0a6dc8e1b8 Support __getitem__ type inference for subscripts (#13579)
## Summary

Follow-up to https://github.com/astral-sh/ruff/pull/13562, to add
support for "arbitrary" subscript operations.
2024-10-01 18:04:16 +00:00
Charlie Marsh
8d54996ffb Avoid indirection in class.__call__ lookup (#13595) 2024-10-01 18:01:36 +00:00
Alex Waygood
73e884b232 [red-knot] [minor] Improve helper methods for builtin types (#13594) 2024-10-01 18:38:33 +01:00
Charlie Marsh
edba60106b Support classes that implement __call__ (#13580)
## Summary

This looked straightforward and removes some TODOs.
2024-10-01 17:15:46 +00:00
Alex Waygood
043fba7a57 [red-knot] Fix a few details around Type::call (#13593) 2024-10-01 16:49:09 +00:00
Alex Waygood
20d997784d ruff_benchmark: open all tomllib files in the red-knot benchmark (#13589) 2024-10-01 17:47:36 +01:00
Alex Waygood
82324678cf Rename the ruff_vendored crate to red_knot_vendored (#13586) 2024-10-01 16:16:59 +01:00
Zanie Blue
cfd5d63917 Use operator specific messaging in division by zero diagnostics (#13588)
Requested at
https://github.com/astral-sh/ruff/pull/13576#discussion_r1782530971
2024-10-01 08:58:38 -05:00
Alex Waygood
2a36b47f13 [red-knot] Remove Type::RevealType (#13567) 2024-10-01 10:01:03 +00:00
Tom Gillam
6322639aca Fix tiny typo in _typos.toml (#13583) 2024-10-01 10:54:00 +01:00
github-actions[bot]
360af1bc32 Sync vendored typeshed stubs (#13578)
Close and reopen this PR to trigger CI

Co-authored-by: typeshedbot <>
2024-10-01 08:05:19 +01:00
Zanie Blue
3af3f74c66 Update dedent_to to support blocks that are composed of comments (#13572)
While looking into https://github.com/astral-sh/ruff/issues/13545 I
noticed that we return `None` here if you pass a block of comments. This
is annoying because it causes `adjust_indentation` to fall back to
LibCST which panics when it cannot find a statement.
2024-10-01 04:38:03 +00:00
Zanie Blue
45f01e7872 Add diagnostic for integer division by zero (#13576)
Adds a diagnostic for division by the integer zero in `//`, `/`, and
`%`.

Doesn't handle `<int> / 0.0` because we don't track the values of float
literals.
2024-09-30 22:38:52 +00:00
Simon
6cdf996af6 [red-knot] feat: introduce a new [Type::Todo] variant (#13548)
This variant shows inference that is not yet implemented..

## Summary

PR #13500 reopened the idea of adding a new type variant to keep track
of not-implemented features in Red Knot.

It was based off of #12986 with a more generic approach of keeping track
of different kind of unknowns. Discussion in #13500 agreed that keeping
track of different `Unknown` is complicated for now, and this feature is
better achieved through a new variant of `Type`.

### Requirements

Requirements for this implementation can be summed up with some extracts
of comment from @carljm on the previous PR

> So at the moment we are leaning towards simplifying this PR to just
use a new top-level variant, which behaves like Any and Unknown but
represents inference that is not yet implemented in red-knot.

> I think the general rule should be that Todo should propagate only
when the presence of the input Todo caused the output to be unknown.
>
> To take a specific example, the inferred result of addition must be
Unknown if either operand is Unknown. That is, Unknown + X will always
be Unknown regardless of what X is. (Same for X + Unknown.) In this
case, I believe that Unknown + Todo (or Todo + Unknown) should result in
Unknown, not result in Todo. If we fix the upstream source of the Todo,
the result would still be Unknown, so it's not useful to propagate the
Todo in this case: it wrongly suggests that the output is unknown
because of a todo item.

## Test Plan

This PR does not introduce new tests, but it did required to edit some
tests with the display of `[Type::Todo]` (currently `@Todo`), which
suggests that those test are placeholders requirements for features we
don't support yet.
2024-09-30 14:28:06 -07:00
Zanie Blue
9d8a4c0057 Improve display of assert_public_ty assertion failures (#13577)
While working on https://github.com/astral-sh/ruff/pull/13576 I noticed
that it was really hard to tell which assertion failed in some of these
test cases. This could be expanded to elsewhere, but I've heard this
test suite format won't be around for long?
2024-09-30 16:12:26 -05:00
Charlie Marsh
c9c748a79e Add some basic subscript type inference (#13562)
## Summary

Just for tuples and strings -- the easiest cases. I think most of the
rest require generic support?
2024-09-30 16:50:46 -04:00
Zanie Blue
32c746bd82 Fix inference when integers are divided (#13575)
Fixes the `Operator::Div` case and adds `Operator::FloorDiv` support

Closes https://github.com/astral-sh/ruff/issues/13570
2024-09-30 15:50:37 -05:00
Zanie Blue
e76f77d711 Use uv in contribution document (#13540) 2024-09-30 14:42:59 -05:00
Charlie Marsh
d86b73eb3d Add unary inference for integer and boolean literals (#13559)
## Summary

Just trying to familiarize myself with the general patterns, testing,
etc.

Part of https://github.com/astral-sh/ruff/issues/12701.
2024-09-30 16:29:06 +00:00
Alex Waygood
5f4b282327 [red-knot] Allow calling bool() with no arguments (#13568) 2024-09-30 13:18:01 +00:00
aditya pillai
d9267132d6 Fix leftover references to red_knot_python_semantic/vendor/ (#13561)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-09-30 11:32:02 +00:00
renovate[bot]
5118166d21 Update NPM Development dependencies (#13560) 2024-09-29 22:09:47 -04:00
renovate[bot]
6fb1d6037a Update pre-commit dependencies (#13558) 2024-09-29 21:50:50 -04:00
renovate[bot]
9237813e0c Update Rust crate tempfile to v3.13.0 (#13557) 2024-09-29 21:50:46 -04:00
renovate[bot]
3bebde3ccc Update Rust crate regex to v1.11.0 (#13556) 2024-09-29 21:50:40 -04:00
renovate[bot]
6c5cbad533 Update dependency ruff to v0.6.8 (#13555) 2024-09-29 21:50:28 -04:00
renovate[bot]
7a2f8d4463 Update dependency react-resizable-panels to v2.1.4 (#13554) 2024-09-29 21:50:22 -04:00
renovate[bot]
ad87ea948d Update Rust crate syn to v2.0.79 (#13553) 2024-09-29 21:50:16 -04:00
renovate[bot]
acfc34d615 Update Rust crate libc to v0.2.159 (#13552) 2024-09-29 21:50:10 -04:00
Charlie Marsh
668730cc28 Link to astral-sh/ruff-action (#13551) 2024-09-29 23:49:24 +00:00
Edouard Choinière
bee498d635 [flake8-use-pathlib] Fix typo in link to Path.stat (PTH116) (#13546)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

There was a typo in the links of the docs of PTH116, where Path.stat
used to link to Path.group.
Another rule, PTH202, does it correctly: 

ec72e675d9/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getsize.rs (L33)

This PR only fixes a one word typo.

## Test Plan

<!-- How was it tested? -->
I did not test that the doc generation framework picked up these
changes, I assume it will do it successfully.
2024-09-28 12:01:41 -04:00
TomerBin
ec72e675d9 Red Knot - Infer the return value of bool() (#13538)
## Summary
Following #13449, this PR adds custom handling for the bool constructor,
so when the input type has statically known truthiness value, it will be
used as the return value of the bool function.
For example, in the following snippet x will now be resolved to
`Literal[True]` instead of `bool`.
```python
x = bool(1)
```

## Test Plan
Some cargo tests were added.
2024-09-27 12:11:55 -07:00
Simon
1639488082 [red-knot] support fstring expressions (#13511)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Implement inference for `f-string`, contributes to #12701.

### First Implementation

When looking at the way `mypy` handles things, I noticed the following:
- No variables (e.g. `f"hello"`) ⇒ `LiteralString`
- Any variable (e.g. `f"number {1}"`) ⇒ `str`

My first commit (1ba5d0f13fdf70ed8b2b1a41433b32fc9085add2) implements
exactly this logic, except that we deal with string literals just like
`infer_string_literal_expression` (if below `MAX_STRING_LITERAL_SIZE`,
show `Literal["exact string"]`)

### Second Implementation

My second commit (90326ce9af5549af7b4efae89cd074ddf68ada14) pushes
things a bit further to handle cases where the expression within the
`f-string` are all literal values (string representation known at static
time).

Here's an example of when this could happen in code:
```python
BASE_URL = "https://httpbin.org"
VERSION = "v1"
endpoint = f"{BASE_URL}/{VERSION}/post"  # Literal["https://httpbin.org/v1/post"]
```
As this can be sightly more costly (additional allocations), I don't
know if we want this feature.

## Test Plan

- Added a test `fstring_expression` covering all cases I can think of

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2024-09-27 10:29:21 -07:00
Micha Reiser
f3e464ea4c refactor: Simplify quote selection logic (#13536) 2024-09-27 14:40:28 +02:00
Micha Reiser
253f5f269a refactor: Rename FormatStringContinuation to FormatImplicitConcatenatedString (#13531) 2024-09-27 08:24:50 +00:00
Micha Reiser
c046101b79 Fix codeblock dynamic line length calculation for indented examples (#13523) 2024-09-27 09:09:07 +02:00
Zanie Blue
7706f561a9 Do not offer an invalid fix for PLR1716 when the comparisons contain parenthesis (#13527)
Related to https://github.com/astral-sh/ruff/issues/13524

Doesn't offer a valid fix, opting to instead just not offer a fix at
all. If someone points me to a good way to handle parenthesis here I'm
down to try to fix the fix separately, but it looks quite hard.
2024-09-26 19:01:06 +00:00
Henry Jiang
f5e3662446 Remove jemalloc crate when building on AIX (#13529)
## Summary
Building ruff on AIX breaks on `tiki-jemalloc-sys` due to OS header
incompatibility

## Test Plan
`cargo test`

Co-authored-by: Henry Jiang <henry.jiang1@ibm.com>
2024-09-26 13:20:54 -04:00
Junzhuo ZHOU
a354d9ead6 Expose internal types as public access (#13509) 2024-09-26 17:34:30 +02:00
Zanie Blue
58a8e9c511 Fix handling of slices in tuples for FURB118, e.g., x[:, 1] (#13518)
There was already handling for the singleton `x[:]` case but not the
tuple case.

Closes https://github.com/astral-sh/ruff/issues/13508
2024-09-26 14:20:03 +00:00
ukyen
e83388dcea Don't raise D208 when last line is non-empty (#13372)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-26 14:53:21 +02:00
Micha Reiser
ae39ce56c0 Bump version to 0.6.8 (#13522) 2024-09-26 14:09:03 +02:00
Micha Reiser
ff2d214e11 Don't skip over imports and other nodes containing nested statements in import collector (#13521) 2024-09-26 11:57:05 +00:00
Micha Reiser
9442cd8fae Parenthesize match..case if guards (#13513) 2024-09-26 06:44:33 +00:00
Micha Reiser
8012707348 Align formatting of patterns in match-cases with expression formatting in clause headers (#13510) 2024-09-26 08:35:22 +02:00
Charlie Marsh
d7ffe46054 Disable the typeset plugin (#13517)
## Summary

There seems to be a bad interaction between enabling anchorlinks and the
`typeset` plugin. I think the former is more important than the
latter... so disabling the latter for now.

## Test Plan

Before:

![Screenshot 2024-09-25 at 7 53
21 PM](https://github.com/user-attachments/assets/bf7c70bb-19ab-4ece-9709-4c297f8ba67b)

After:

![Screenshot 2024-09-25 at 7 53
12 PM](https://github.com/user-attachments/assets/e767a575-1664-4288-aecb-82e8b1b1a7bd)
2024-09-25 23:58:35 +00:00
haarisr
7c83af419c red-knot: Implement the not operator for all Type variants (#13432)
Signed-off-by: haaris <haarisrahman@gmail.com>
Co-authored-by: Carl Meyer <carl@oddbird.net>
2024-09-25 13:44:19 -07:00
Zanie Blue
bbb044ebda Detect tuples bound to variadic positional arguments i.e. *args (#13512)
In https://github.com/astral-sh/ruff/pull/13503, we added supported for
detecting variadic keyword arguments as dictionaries, here we use the
same strategy for detecting variadic positional arguments as tuples.
2024-09-25 10:03:25 -05:00
Zanie Blue
481065238b Avoid UP028 false negatives with non-reference shadowed bindings of loop variables (#13504)
Closes https://github.com/astral-sh/ruff/issues/13266

Avoids false negatives for shadowed bindings that aren't actually
references to the loop variable. There are some shadowed bindings we
need to support still, e.g., `del` requires the loop variable to exist.
2024-09-25 10:03:09 -05:00
Zanie Blue
11f06e0d55 Detect SIM910 when using variadic keyword arguments, i.e., **kwargs (#13503)
Closes https://github.com/astral-sh/ruff/issues/13493
2024-09-25 10:02:59 -05:00
Dylan
f27a8b8c7a [internal] ComparableExpr (f)strings and bytes made invariant under concatenation (#13301) 2024-09-25 16:58:57 +02:00
Vince van Noort
ca0ae0a484 [pylint] Implement boolean-chained-comparison (R1716) (#13435)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-09-25 09:14:12 +00:00
TomerBin
be1d5e3368 [red-knot] Add Type::bool and boolean expression inference (#13449) 2024-09-25 00:02:26 +00:00
Simon Brugman
03503f7f56 C401 message missing closing parenthesis (#13498) 2024-09-24 14:55:32 +02:00
Charlie Marsh
ff4b6d11fa Detect basic wildcard imports in ruff analyze graph (#13486)
## Summary

I guess we can just ignore the `*` entirely for now? This will add the
`__init__.py` for anything that's importing a package.
2024-09-23 18:09:00 -04:00
Charlie Marsh
96e7f3f96f Exit gracefully on broken pipe errors (#13485)
## Summary

Closes https://github.com/astral-sh/ruff/issues/13483.

Closes https://github.com/astral-sh/ruff/issues/13442.

## Test Plan

```
❯ cargo run analyze graph ../django | head -n 10
   Compiling ruff v0.6.7 (/Users/crmarsh/workspace/ruff/crates/ruff)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.63s
     Running `target/debug/ruff analyze graph ../django`
warning: `ruff analyze graph` is experimental and may change without warning
{
  "/Users/crmarsh/workspace/django/django/__init__.py": [
    "/Users/crmarsh/workspace/django/django/apps/__init__.py",
    "/Users/crmarsh/workspace/django/django/conf/__init__.py",
    "/Users/crmarsh/workspace/django/django/urls/__init__.py",
    "/Users/crmarsh/workspace/django/django/utils/log.py",
    "/Users/crmarsh/workspace/django/django/utils/version.py"
  ],
  "/Users/crmarsh/workspace/django/django/__main__.py": [
    "/Users/crmarsh/workspace/django/django/core/management/__init__.py"
```
2024-09-23 13:48:43 +00:00
Charlie Marsh
90dc7438ee Avoid panic when analyze graph hits broken pipe (#13484)
## Summary

I think we should also make the change that @BurntSushi recommended in
the linked issue, but this gets rid of the panic.

See: https://github.com/astral-sh/ruff/issues/13483

See: https://github.com/astral-sh/ruff/issues/13442

## Test Plan

```
warning: `ruff analyze graph` is experimental and may change without warning
{
  "/Users/crmarsh/workspace/django/django/__init__.py": [
    "/Users/crmarsh/workspace/django/django/apps/__init__.py",
    "/Users/crmarsh/workspace/django/django/conf/__init__.py",
    "/Users/crmarsh/workspace/django/django/urls/__init__.py",
    "/Users/crmarsh/workspace/django/django/utils/log.py",
    "/Users/crmarsh/workspace/django/django/utils/version.py"
  ],
  "/Users/crmarsh/workspace/django/django/__main__.py": [
    "/Users/crmarsh/workspace/django/django/core/management/__init__.py"
ruff failed
  Cause: Broken pipe (os error 32)
```
2024-09-23 09:43:09 -04:00
Micha Reiser
3e99ab141c Update Salsa (#13480) 2024-09-23 14:04:04 +02:00
renovate[bot]
115745a8ac Update dependency monaco-editor to ^0.52.0 (#13475)
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [monaco-editor](https://redirect.github.com/microsoft/monaco-editor) |
[`^0.51.0` ->
`^0.52.0`](https://renovatebot.com/diffs/npm/monaco-editor/0.51.0/0.52.0)
|
[![age](https://developer.mend.io/api/mc/badges/age/npm/monaco-editor/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/monaco-editor/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/monaco-editor/0.51.0/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/monaco-editor/0.51.0/0.52.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>microsoft/monaco-editor (monaco-editor)</summary>

###
[`v0.52.0`](https://redirect.github.com/microsoft/monaco-editor/blob/HEAD/CHANGELOG.md#0520)

[Compare
Source](https://redirect.github.com/microsoft/monaco-editor/compare/v0.51.0...v0.52.0)

-   Comment added inside of `IModelContentChangedEvent`

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC44MC4wIiwidXBkYXRlZEluVmVyIjoiMzguODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:55:12 +02:00
renovate[bot]
8bb59d7216 Update Rust crate unicode_names2 to v1.3.0 (#13474)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [unicode_names2](https://redirect.github.com/progval/unicode_names2) |
workspace.dependencies | minor | `1.2.2` -> `1.3.0` |

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC44MC4wIiwidXBkYXRlZEluVmVyIjoiMzguODAuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:54:44 +02:00
renovate[bot]
47aac060de Update Rust crate insta to v1.40.0 (#13472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 09:40:02 +02:00
Steve C
7c55330534 Fix formatting for analyze direction values (#13476) 2024-09-23 09:18:28 +02:00
renovate[bot]
047d77c60b Update pre-commit dependencies (#13467) 2024-09-22 22:54:34 -04:00
renovate[bot]
18fddd458a Update dependency eslint to v8.57.1 (#13465) 2024-09-22 22:54:14 -04:00
Charlie Marsh
db76000521 Use anchorlinks rather than permalinks (#13471)
## Summary

See: https://github.com/astral-sh/uv/pull/7626
2024-09-23 02:44:45 +00:00
renovate[bot]
a2ed1e1cd1 Update Rust crate thiserror to v1.0.64 (#13462) 2024-09-22 22:32:45 -04:00
renovate[bot]
7457679582 Update Rust crate dashmap to v6.1.0 (#13470) 2024-09-22 22:32:26 -04:00
renovate[bot]
1d352872ba Update Rust crate codspeed-criterion-compat to v2.7.2 (#13469) 2024-09-22 22:32:20 -04:00
renovate[bot]
c8b905bc96 Update NPM Development dependencies (#13468) 2024-09-22 22:32:11 -04:00
renovate[bot]
5b593d0397 Update dependency ruff to v0.6.7 (#13466) 2024-09-22 22:32:02 -04:00
renovate[bot]
c5c5acda23 Update Rust crate unicode-normalization to v0.1.24 (#13464) 2024-09-22 22:31:53 -04:00
renovate[bot]
26747aae75 Update Rust crate unicode-ident to v1.0.13 (#13463) 2024-09-22 22:31:47 -04:00
renovate[bot]
85b825a2a1 Update Rust crate syn to v2.0.77 (#13461) 2024-09-22 22:31:40 -04:00
renovate[bot]
9e764ef6d0 Update Rust crate serde_json to v1.0.128 (#13460) 2024-09-23 02:03:47 +00:00
renovate[bot]
0e325a53ef Update Rust crate serde to v1.0.210 (#13459) 2024-09-23 02:03:15 +00:00
renovate[bot]
2a136cfb57 Update Rust crate pretty_assertions to v1.4.1 (#13458) 2024-09-23 02:02:12 +00:00
renovate[bot]
7749164d4a Update Rust crate ordermap to v0.5.3 (#13457) 2024-09-23 02:01:44 +00:00
renovate[bot]
da50e14524 Update Rust crate lsp-server to v0.7.7 (#13456) 2024-09-23 02:00:23 +00:00
renovate[bot]
1886b731a5 Update Rust crate ignore to v0.4.23 (#13455) 2024-09-22 22:00:06 -04:00
renovate[bot]
364eddc95a Update Rust crate globset to v0.4.15 (#13454) 2024-09-22 22:00:01 -04:00
renovate[bot]
48fb340e3b Update Rust crate filetime to v0.2.25 (#13453) 2024-09-22 21:59:50 -04:00
renovate[bot]
71bb4d3bdc Update Rust crate clap to v4.5.18 (#13452) 2024-09-22 21:59:44 -04:00
renovate[bot]
5c20f570d0 Update Rust crate anyhow to v1.0.89 (#13451) 2024-09-23 01:58:14 +00:00
Charlie Marsh
7441da287f Skip traversal for non-compound statements (#13441)
## Summary

None of these can contain imports.
2024-09-21 20:47:30 +00:00
Charlie Marsh
c2a5179d75 Reuse BTreeSets in module resolver (#13440)
## Summary

For dependencies, there's no reason to re-allocate here, since we know
the paths are unique.
2024-09-21 20:14:32 +00:00
Charlie Marsh
17c4690b5e Bump version to v0.6.7 (#13439) 2024-09-21 13:16:36 -04:00
Charlie Marsh
f06d44e6e5 Use forget for module resolver database (#13438)
## Summary

A tiny bit faster and the `red-knot` CLI does the same thing.
2024-09-21 17:00:02 +00:00
Micha Reiser
653c09001a Use an empty vendored file system in Ruff (#13436)
## Summary

This PR changes removes the typeshed stubs from the vendored file system
shipped with ruff
and instead ships an empty "typeshed".

Making the typeshed files optional required extracting the typshed files
into a new `ruff_vendored` crate. I do like this even if all our builds
always include typeshed because it means `red_knot_python_semantic`
contains less code that needs compiling.

This also allows us to use deflate because the compression algorithm
doesn't matter for an archive containing a single, empty file.

## Test Plan

`cargo test`

I verified with ` cargo tree -f "{p} {f}" -p <package> ` that:

* red_knot_wasm: enables `deflate` compression
* red_knot: enables `zstd` compression
* `ruff`: uses stored


I'm not quiet sure how to build the binary that maturin builds but
comparing the release artifact size with `strip = true` shows a `1.5MB`
size reduction

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-09-21 16:31:42 +00:00
Micha Reiser
8921fbb54c vendored_typeshed_versions should use db.vendored (#13434) 2024-09-21 16:35:06 +02:00
Charlie Marsh
3018303c87 Avoid parsing with Salsa (#13437)
## Summary

For reasons I haven't investigated, this speeds up the resolver about 2x
(from 6.404s to 3.612s on an extremely large codebase).

## Test Plan

\cc @BurntSushi 

```
[andrew@duff rippling]$ time ruff analyze graph --preview > /dev/null

real    3.274
user    16.039
sys     7.609
maxmem  11631 MB
faults  0
[andrew@duff rippling]$ time ruff-patch analyze graph --preview > /dev/null

real    1.841
user    14.625
sys     3.639
maxmem  7173 MB
faults  0
[andrew@duff rippling]$ time ruff-patch2 analyze graph --preview > /dev/null

real    2.087
user    15.333
sys     4.869
maxmem  8642 MB
faults  0
```

Where that's `main`, then (`ruff-patch`) using the version with no
`File`, no `SemanticModel`, then (`ruff-patch2`) using `File`.
2024-09-21 13:52:16 +00:00
haarisr
6c303b2445 red-knot: Add not unary operator for boolean literals (#13422)
## Summary

Contributes to #12701

## Test Plan

Added test for boolean literals

Signed-off-by: haaris <haarisrahman@gmail.com>
2024-09-20 15:24:38 -07:00
886 changed files with 19736 additions and 6415 deletions

View File

@@ -148,7 +148,7 @@ jobs:
# sync, not just public items. Eventually we should do this for all
# crates; for now add crates here as they are warning-clean to prevent
# regression.
- run: cargo doc --no-deps -p red_knot_python_semantic -p red_knot -p ruff_db --document-private-items
- run: cargo doc --no-deps -p red_knot_python_semantic -p red_knot -p red_knot_test -p ruff_db --document-private-items
env:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings"
@@ -518,6 +518,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@v0.9.0
@@ -525,13 +527,15 @@ jobs:
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
- name: "Install Rust toolchain"
run: rustup show
- name: Install uv
uses: astral-sh/setup-uv@v3
- uses: Swatinem/rust-cache@v2
- name: "Install Insiders dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
run: pip install -r docs/requirements-insiders.txt
run: uv pip install -r docs/requirements-insiders.txt --system
- name: "Install dependencies"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
run: pip install -r docs/requirements.txt
run: uv pip install -r docs/requirements.txt --system
- name: "Update README File"
run: python scripts/transform_readme.py --target mkdocs
- name: "Generate docs"
@@ -608,7 +612,7 @@ jobs:
just test
benchmarks:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: determine_changes
if: ${{ github.repository == 'astral-sh/ruff' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
timeout-minutes: 20

View File

@@ -37,13 +37,13 @@ jobs:
- name: Sync typeshed
id: sync
run: |
rm -rf ruff/crates/red_knot_python_semantic/vendor/typeshed
mkdir ruff/crates/red_knot_python_semantic/vendor/typeshed
cp typeshed/README.md ruff/crates/red_knot_python_semantic/vendor/typeshed
cp typeshed/LICENSE ruff/crates/red_knot_python_semantic/vendor/typeshed
cp -r typeshed/stdlib ruff/crates/red_knot_python_semantic/vendor/typeshed/stdlib
rm -rf ruff/crates/red_knot_python_semantic/vendor/typeshed/stdlib/@tests
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_python_semantic/vendor/typeshed/source_commit.txt
rm -rf ruff/crates/red_knot_vendored/vendor/typeshed
mkdir ruff/crates/red_knot_vendored/vendor/typeshed
cp typeshed/README.md ruff/crates/red_knot_vendored/vendor/typeshed
cp typeshed/LICENSE ruff/crates/red_knot_vendored/vendor/typeshed
cp -r typeshed/stdlib ruff/crates/red_knot_vendored/vendor/typeshed/stdlib
rm -rf ruff/crates/red_knot_vendored/vendor/typeshed/stdlib/@tests
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_vendored/vendor/typeshed/source_commit.txt
- name: Commit the changes
id: commit
if: ${{ steps.sync.outcome == 'success' }}

View File

@@ -2,7 +2,7 @@ fail_fast: true
exclude: |
(?x)^(
crates/red_knot_python_semantic/vendor/.*|
crates/red_knot_vendored/vendor/.*|
crates/red_knot_workspace/resources/.*|
crates/ruff_linter/resources/.*|
crates/ruff_linter/src/rules/.*/snapshots/.*|
@@ -17,7 +17,7 @@ exclude: |
repos:
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.19
rev: v0.20.2
hooks:
- id: validate-pyproject
@@ -28,6 +28,7 @@ repos:
additional_dependencies:
- mdformat-mkdocs
- mdformat-admon
- mdformat-footnote
exclude: |
(?x)^(
docs/formatter/black\.md
@@ -35,7 +36,7 @@ repos:
)$
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.41.0
rev: v0.42.0
hooks:
- id: markdownlint-fix
exclude: |
@@ -45,7 +46,7 @@ repos:
)$
- repo: https://github.com/crate-ci/typos
rev: v1.24.5
rev: v1.25.0
hooks:
- id: typos
@@ -59,7 +60,7 @@ repos:
pass_filenames: false # This makes it a lot faster
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
rev: v0.6.9
hooks:
- id: ruff-format
- id: ruff
@@ -68,8 +69,8 @@ repos:
require_serial: true
# Prettier
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.3.3
hooks:
- id: prettier
types: [yaml]

View File

@@ -1,5 +1,79 @@
# Changelog
## 0.6.9
### Preview features
- Fix codeblock dynamic line length calculation for indented docstring examples ([#13523](https://github.com/astral-sh/ruff/pull/13523))
- \[`refurb`\] Mark `FURB118` fix as unsafe ([#13613](https://github.com/astral-sh/ruff/pull/13613))
### Rule changes
- \[`pydocstyle`\] Don't raise `D208` when last line is non-empty ([#13372](https://github.com/astral-sh/ruff/pull/13372))
- \[`pylint`\] Preserve trivia (i.e. comments) in `PLR5501` autofix ([#13573](https://github.com/astral-sh/ruff/pull/13573))
### Configuration
- \[`pyflakes`\] Add `allow-unused-imports` setting for `unused-import` rule (`F401`) ([#13601](https://github.com/astral-sh/ruff/pull/13601))
### Bug fixes
- Support ruff discovery in pip build environments ([#13591](https://github.com/astral-sh/ruff/pull/13591))
- \[`flake8-bugbear`\] Avoid short circuiting `B017` for multiple context managers ([#13609](https://github.com/astral-sh/ruff/pull/13609))
- \[`pylint`\] Do not offer an invalid fix for `PLR1716` when the comparisons contain parenthesis ([#13527](https://github.com/astral-sh/ruff/pull/13527))
- \[`pyupgrade`\] Fix `UP043` to apply to `collections.abc.Generator` and `collections.abc.AsyncGenerator` ([#13611](https://github.com/astral-sh/ruff/pull/13611))
- \[`refurb`\] Fix handling of slices in tuples for `FURB118`, e.g., `x[:, 1]` ([#13518](https://github.com/astral-sh/ruff/pull/13518))
### Documentation
- Update GitHub Action link to `astral-sh/ruff-action` ([#13551](https://github.com/astral-sh/ruff/pull/13551))
## 0.6.8
### Preview features
- Remove unnecessary parentheses around `match case` clauses ([#13510](https://github.com/astral-sh/ruff/pull/13510))
- Parenthesize overlong `if` guards in `match..case` clauses ([#13513](https://github.com/astral-sh/ruff/pull/13513))
- Detect basic wildcard imports in `ruff analyze graph` ([#13486](https://github.com/astral-sh/ruff/pull/13486))
- \[`pylint`\] Implement `boolean-chained-comparison` (`R1716`) ([#13435](https://github.com/astral-sh/ruff/pull/13435))
### Rule changes
- \[`lake8-simplify`\] Detect `SIM910` when using variadic keyword arguments, i.e., `**kwargs` ([#13503](https://github.com/astral-sh/ruff/pull/13503))
- \[`pyupgrade`\] Avoid false negatives with non-reference shadowed bindings of loop variables (`UP028`) ([#13504](https://github.com/astral-sh/ruff/pull/13504))
### Bug fixes
- Detect tuples bound to variadic positional arguments i.e. `*args` ([#13512](https://github.com/astral-sh/ruff/pull/13512))
- Exit gracefully on broken pipe errors ([#13485](https://github.com/astral-sh/ruff/pull/13485))
- Avoid panic when analyze graph hits broken pipe ([#13484](https://github.com/astral-sh/ruff/pull/13484))
### Performance
- Reuse `BTreeSets` in module resolver ([#13440](https://github.com/astral-sh/ruff/pull/13440))
- Skip traversal for non-compound statements ([#13441](https://github.com/astral-sh/ruff/pull/13441))
## 0.6.7
### Preview features
- Add Python version support to ruff analyze CLI ([#13426](https://github.com/astral-sh/ruff/pull/13426))
- Add `exclude` support to `ruff analyze` ([#13425](https://github.com/astral-sh/ruff/pull/13425))
- Fix parentheses around return type annotations ([#13381](https://github.com/astral-sh/ruff/pull/13381))
### Rule changes
- \[`pycodestyle`\] Fix: Don't autofix if the first line ends in a question mark? (D400) ([#13399](https://github.com/astral-sh/ruff/pull/13399))
### Bug fixes
- Respect `lint.exclude` in ruff check `--add-noqa` ([#13427](https://github.com/astral-sh/ruff/pull/13427))
### Performance
- Avoid tracking module resolver files in Salsa ([#13437](https://github.com/astral-sh/ruff/pull/13437))
- Use `forget` for module resolver database ([#13438](https://github.com/astral-sh/ruff/pull/13438))
## 0.6.6
### Preview features

View File

@@ -29,16 +29,14 @@ You'll also need [Insta](https://insta.rs/docs/) to update snapshot tests:
cargo install cargo-insta
```
And you'll need pre-commit to run some validation checks:
```shell
pipx install pre-commit # or `pip install pre-commit` if you have a virtualenv
```
You'll need [uv](https://docs.astral.sh/uv/getting-started/installation/) (or `pipx` and `pip`) to
run Python utility commands.
You can optionally install pre-commit hooks to automatically run the validation checks
when making a commit:
```shell
uv tool install pre-commit
pre-commit install
```
@@ -66,7 +64,7 @@ and that it passes both the lint and test validation checks:
```shell
cargo clippy --workspace --all-targets --all-features -- -D warnings # Rust linting
RUFF_UPDATE_SCHEMA=1 cargo test # Rust testing and updating ruff.schema.json
pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
uvx pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
```
These checks will run on GitHub Actions when you open your pull request, but running them locally
@@ -267,26 +265,20 @@ To preview any changes to the documentation locally:
1. Install the [Rust toolchain](https://www.rust-lang.org/tools/install).
1. Install MkDocs and Material for MkDocs with:
```shell
pip install -r docs/requirements.txt
```
1. Generate the MkDocs site with:
```shell
python scripts/generate_mkdocs.py
uv run --no-project --isolated --with-requirements docs/requirements.txt scripts/generate_mkdocs.py
```
1. Run the development server with:
```shell
# For contributors.
mkdocs serve -f mkdocs.public.yml
uvx --with-requirements docs/requirements.txt -- mkdocs serve -f mkdocs.public.yml
# For members of the Astral org, which has access to MkDocs Insiders via sponsorship.
mkdocs serve -f mkdocs.insiders.yml
uvx --with-requirements docs/requirements-insiders.txt -- mkdocs serve -f mkdocs.insiders.yml
```
The documentation should then be available locally at
@@ -368,9 +360,8 @@ GitHub Actions will run your changes against a number of real-world projects fro
report on any linter or formatter differences. You can also run those checks locally via:
```shell
pip install -e ./python/ruff-ecosystem
ruff-ecosystem check ruff "./target/debug/ruff"
ruff-ecosystem format ruff "./target/debug/ruff"
uvx --from ./python/ruff-ecosystem ruff-ecosystem check ruff "./target/debug/ruff"
uvx --from ./python/ruff-ecosystem ruff-ecosystem format ruff "./target/debug/ruff"
```
See the [ruff-ecosystem package](https://github.com/astral-sh/ruff/tree/main/python/ruff-ecosystem) for more details.

372
Cargo.lock generated
View File

@@ -36,12 +36,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "android-tzdata"
version = "0.1.1"
@@ -129,9 +123,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.86"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
[[package]]
name = "append-only-vec"
@@ -225,7 +219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [
"memchr",
"regex-automata 0.4.6",
"regex-automata 0.4.8",
"serde",
]
@@ -353,9 +347,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.16"
version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
dependencies = [
"clap_builder",
"clap_derive",
@@ -363,9 +357,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.15"
version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
dependencies = [
"anstream",
"anstyle",
@@ -406,9 +400,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.13"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [
"heck",
"proc-macro2",
@@ -437,9 +431,9 @@ dependencies = [
[[package]]
name = "codspeed"
version = "2.6.0"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a104ac948e0188b921eb3fcbdd55dcf62e542df4c7ab7e660623f6288302089"
checksum = "450a0e9df9df1c154156f4344f99d8f6f6e69d0fc4de96ef6e2e68b2ec3bce97"
dependencies = [
"colored",
"libc",
@@ -448,9 +442,9 @@ dependencies = [
[[package]]
name = "codspeed-criterion-compat"
version = "2.6.0"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "722c36bdc62d9436d027256ce2627af81ac7a596dfc7d13d849d0d212448d7fe"
checksum = "8eb1a6cb9c20e177fde58cdef97c1c7c9264eb1424fe45c4fccedc2fb078a569"
dependencies = [
"codspeed",
"colored",
@@ -714,7 +708,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown",
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
@@ -722,13 +716,13 @@ dependencies = [
[[package]]
name = "dashmap"
version = "6.0.1"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
"cfg-if",
"crossbeam-utils",
"hashbrown",
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
@@ -879,9 +873,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.0.2"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]]
name = "fern"
@@ -894,9 +888,9 @@ dependencies = [
[[package]]
name = "filetime"
version = "0.2.24"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
dependencies = [
"cfg-if",
"libc",
@@ -987,15 +981,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
version = "0.4.14"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata 0.4.6",
"regex-syntax 0.8.3",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]]
@@ -1026,16 +1020,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown",
"hashbrown 0.14.5",
]
[[package]]
@@ -1106,15 +1105,15 @@ dependencies = [
[[package]]
name = "ignore"
version = "0.4.22"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata 0.4.6",
"regex-automata 0.4.8",
"same-file",
"walkdir",
"winapi-util",
@@ -1127,7 +1126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc9da1a252bd44cd341657203722352efc9bc0c847d06ea6d2dc1cd1135e0a01"
dependencies = [
"ahash",
"hashbrown",
"hashbrown 0.14.5",
]
[[package]]
@@ -1142,12 +1141,12 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.4.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.14.5",
"serde",
]
@@ -1193,9 +1192,9 @@ dependencies = [
[[package]]
name = "insta"
version = "1.39.0"
version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5"
checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60"
dependencies = [
"console",
"globset",
@@ -1312,9 +1311,9 @@ checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
[[package]]
name = "js-sys"
version = "0.3.70"
version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
dependencies = [
"wasm-bindgen",
]
@@ -1347,15 +1346,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.158"
version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]]
name = "libcst"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10293a04a48e8b0cb2cc825a93b83090e527bffd3c897a0255ad7bc96079e920"
checksum = "1586dd7a857d8a61a577afde1a24cc9573ff549eff092d5ce968b1ec93cc61b6"
dependencies = [
"chic",
"libcst_derive",
@@ -1405,9 +1404,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_api"
@@ -1427,9 +1426,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lsp-server"
version = "0.7.6"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095"
checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9"
dependencies = [
"crossbeam-channel",
"log",
@@ -1626,9 +1625,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "oorandom"
@@ -1644,9 +1643,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordermap"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61d7d835be600a7ac71b24e39c92fe6fad9e818b3c71bfc379e3ba65e327d77f"
checksum = "31f2bd7b03bf2c767e1bb7b91505dbe022833776e60480275e6f2fb0db0c7503"
dependencies = [
"indexmap",
]
@@ -1691,9 +1690,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.14"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "path-absolutize"
@@ -1721,15 +1720,15 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
[[package]]
name = "pathdiff"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
[[package]]
name = "peg"
version = "0.8.2"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "400bcab7d219c38abf8bd7cc2054eb9bbbd4312d66f6a5557d572a203f646f61"
checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f"
dependencies = [
"peg-macros",
"peg-runtime",
@@ -1737,9 +1736,9 @@ dependencies = [
[[package]]
name = "peg-macros"
version = "0.8.2"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46e61cce859b76d19090f62da50a9fe92bab7c2a5f09e183763559a2ac392c90"
checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426"
dependencies = [
"peg-runtime",
"proc-macro2",
@@ -1748,9 +1747,9 @@ dependencies = [
[[package]]
name = "peg-runtime"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36bae92c60fa2398ce4678b98b2c4b5a7c61099961ca1fa305aec04a9ad28922"
checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a"
[[package]]
name = "pep440_rs"
@@ -1934,9 +1933,9 @@ dependencies = [
[[package]]
name = "pretty_assertions"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
@@ -1944,9 +1943,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.86"
version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
dependencies = [
"unicode-ident",
]
@@ -2081,11 +2080,14 @@ dependencies = [
"camino",
"compact_str",
"countme",
"hashbrown",
"hashbrown 0.15.0",
"insta",
"once_cell",
"itertools 0.13.0",
"memchr",
"ordermap",
"path-slash",
"red_knot_test",
"red_knot_vendored",
"rstest",
"ruff_db",
"ruff_index",
"ruff_python_ast",
@@ -2102,8 +2104,6 @@ dependencies = [
"test-case",
"thiserror",
"tracing",
"walkdir",
"zip",
]
[[package]]
@@ -2130,6 +2130,37 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "red_knot_test"
version = "0.0.0"
dependencies = [
"anyhow",
"colored",
"once_cell",
"red_knot_python_semantic",
"red_knot_vendored",
"regex",
"ruff_db",
"ruff_index",
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.0.0",
"salsa",
"smallvec",
]
[[package]]
name = "red_knot_vendored"
version = "0.0.0"
dependencies = [
"once_cell",
"path-slash",
"ruff_db",
"walkdir",
"zip",
]
[[package]]
name = "red_knot_wasm"
version = "0.0.0"
@@ -2155,6 +2186,7 @@ dependencies = [
"notify",
"rayon",
"red_knot_python_semantic",
"red_knot_vendored",
"ruff_cache",
"ruff_db",
"ruff_python_ast",
@@ -2196,14 +2228,14 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.6"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.6",
"regex-syntax 0.8.3",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]]
@@ -2217,13 +2249,13 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.6"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.3",
"regex-syntax 0.8.5",
]
[[package]]
@@ -2234,9 +2266,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.3"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "ring"
@@ -2253,9 +2291,36 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rstest"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b423f0e62bdd61734b67cd21ff50871dfaeb9cc74f869dcd6af974fbcb19936"
dependencies = [
"rstest_macros",
"rustc_version",
]
[[package]]
name = "rstest_macros"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e1711e7d14f74b12a58411c542185ef7fb7f2e7f8ee6e2940a883628522b42"
dependencies = [
"cfg-if",
"glob",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn",
"unicode-ident",
]
[[package]]
name = "ruff"
version = "0.6.6"
version = "0.6.9"
dependencies = [
"anyhow",
"argfile",
@@ -2327,6 +2392,7 @@ dependencies = [
"ruff_python_formatter",
"ruff_python_parser",
"ruff_python_trivia",
"rustc-hash 2.0.0",
"serde",
"serde_json",
"tikv-jemallocator",
@@ -2353,7 +2419,7 @@ version = "0.0.0"
dependencies = [
"camino",
"countme",
"dashmap 6.0.1",
"dashmap 6.1.0",
"filetime",
"ignore",
"insta",
@@ -2402,7 +2468,6 @@ dependencies = [
"ruff_python_codegen",
"ruff_python_formatter",
"ruff_python_parser",
"ruff_python_stdlib",
"ruff_python_trivia",
"ruff_workspace",
"schemars",
@@ -2450,15 +2515,18 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"once_cell",
"red_knot_python_semantic",
"ruff_cache",
"ruff_db",
"ruff_linter",
"ruff_macros",
"ruff_python_ast",
"ruff_python_parser",
"salsa",
"schemars",
"serde",
"zip",
]
[[package]]
@@ -2471,7 +2539,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.6.6"
version = "0.6.9"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.2",
@@ -2765,6 +2833,7 @@ dependencies = [
"serde",
"serde_json",
"shellexpand",
"thiserror",
"tracing",
"tracing-subscriber",
]
@@ -2791,7 +2860,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.6.6"
version = "0.6.9"
dependencies = [
"console_error_panic_hook",
"console_log",
@@ -2873,10 +2942,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustix"
version = "0.38.34"
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [
"bitflags 2.6.0",
"errno",
@@ -2932,12 +3010,12 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "salsa"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=f608ff8b24f07706492027199f51132244034f29#f608ff8b24f07706492027199f51132244034f29"
source = "git+https://github.com/salsa-rs/salsa.git?rev=b14be5c0392f4c55eca60b92e457a35549372382#b14be5c0392f4c55eca60b92e457a35549372382"
dependencies = [
"append-only-vec",
"arc-swap",
"crossbeam",
"dashmap 6.0.1",
"dashmap 6.1.0",
"hashlink",
"indexmap",
"lazy_static",
@@ -2952,12 +3030,12 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
version = "0.1.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=f608ff8b24f07706492027199f51132244034f29#f608ff8b24f07706492027199f51132244034f29"
source = "git+https://github.com/salsa-rs/salsa.git?rev=b14be5c0392f4c55eca60b92e457a35549372382#b14be5c0392f4c55eca60b92e457a35549372382"
[[package]]
name = "salsa-macros"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=f608ff8b24f07706492027199f51132244034f29#f608ff8b24f07706492027199f51132244034f29"
source = "git+https://github.com/salsa-rs/salsa.git?rev=b14be5c0392f4c55eca60b92e457a35549372382#b14be5c0392f4c55eca60b92e457a35549372382"
dependencies = [
"heck",
"proc-macro2",
@@ -3018,10 +3096,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "serde"
version = "1.0.209"
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
@@ -3039,9 +3123,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.209"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
@@ -3061,9 +3145,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.127"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
@@ -3102,9 +3186,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.9.0"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817"
dependencies = [
"serde",
"serde_derive",
@@ -3113,9 +3197,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.9.0"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d"
dependencies = [
"darling",
"proc-macro2",
@@ -3233,9 +3317,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "2.0.76"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
@@ -3255,9 +3339,9 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.12.0"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
dependencies = [
"cfg-if",
"fastrand",
@@ -3268,12 +3352,12 @@ dependencies = [
[[package]]
name = "terminal_size"
version = "0.3.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
dependencies = [
"rustix",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3330,18 +3414,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.63"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
@@ -3602,15 +3686,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-normalization"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
dependencies = [
"tinyvec",
]
@@ -3623,9 +3707,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "unicode_names2"
version = "1.2.2"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addeebf294df7922a1164f729fb27ebbbcea99cc32b3bf08afab62757f707677"
checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd"
dependencies = [
"phf",
"unicode_names2_generator",
@@ -3633,9 +3717,9 @@ dependencies = [
[[package]]
name = "unicode_names2_generator"
version = "1.2.2"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f444b8bba042fe3c1251ffaca35c603f2dc2ccc08d595c65a8c4f76f3e8426c0"
checksum = "b91e5b84611016120197efd7dc93ef76774f4e084cd73c9fb3ea4a86c570c56e"
dependencies = [
"getopts",
"log",
@@ -3775,9 +3859,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
dependencies = [
"cfg-if",
"once_cell",
@@ -3786,9 +3870,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
dependencies = [
"bumpalo",
"log",
@@ -3801,9 +3885,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.43"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
dependencies = [
"cfg-if",
"js-sys",
@@ -3813,9 +3897,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3823,9 +3907,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
@@ -3836,15 +3920,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.43"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9"
checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426"
dependencies = [
"console_error_panic_hook",
"js-sys",
@@ -3857,9 +3941,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.43"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021"
checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0"
dependencies = [
"proc-macro2",
"quote",
@@ -4121,9 +4205,9 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "yansi"
version = "0.5.1"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yansi-term"

View File

@@ -14,7 +14,7 @@ license = "MIT"
[workspace.dependencies]
ruff = { path = "crates/ruff" }
ruff_cache = { path = "crates/ruff_cache" }
ruff_db = { path = "crates/ruff_db" }
ruff_db = { path = "crates/ruff_db", default-features = false }
ruff_diagnostics = { path = "crates/ruff_diagnostics" }
ruff_formatter = { path = "crates/ruff_formatter" }
ruff_graph = { path = "crates/ruff_graph" }
@@ -34,11 +34,13 @@ ruff_python_trivia = { path = "crates/ruff_python_trivia" }
ruff_server = { path = "crates/ruff_server" }
ruff_source_file = { path = "crates/ruff_source_file" }
ruff_text_size = { path = "crates/ruff_text_size" }
red_knot_vendored = { path = "crates/red_knot_vendored" }
ruff_workspace = { path = "crates/ruff_workspace" }
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
red_knot_server = { path = "crates/red_knot_server" }
red_knot_workspace = { path = "crates/red_knot_workspace" }
red_knot_test = { path = "crates/red_knot_test" }
red_knot_workspace = { path = "crates/red_knot_workspace", default-features = false }
aho-corasick = { version = "1.1.3" }
annotate-snippets = { version = "0.9.2", features = ["color"] }
@@ -71,7 +73,10 @@ filetime = { version = "0.2.23" }
glob = { version = "0.3.1" }
globset = { version = "0.4.14" }
globwalk = { version = "0.9.1" }
hashbrown = "0.14.3"
hashbrown = { version = "0.15.0", default-features = false, features = [
"raw-entry",
"inline-more",
] }
ignore = { version = "0.4.22" }
imara-diff = { version = "0.1.5" }
imperative = { version = "1.0.4" }
@@ -89,7 +94,7 @@ libcst = { version = "1.1.0", default-features = false }
log = { version = "0.4.17" }
lsp-server = { version = "0.7.6" }
lsp-types = { git = "https://github.com/astral-sh/lsp-types.git", rev = "3512a9f", features = [
"proposed",
"proposed",
] }
matchit = { version = "0.8.1" }
memchr = { version = "2.7.1" }
@@ -110,8 +115,9 @@ quote = { version = "1.0.23" }
rand = { version = "0.8.5" }
rayon = { version = "1.10.0" }
regex = { version = "1.10.2" }
rstest = { version = "0.22.0", default-features = false }
rustc-hash = { version = "2.0.0" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "f608ff8b24f07706492027199f51132244034f29" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "b14be5c0392f4c55eca60b92e457a35549372382" }
schemars = { version = "0.8.16" }
seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }
@@ -119,7 +125,7 @@ serde-wasm-bindgen = { version = "0.6.4" }
serde_json = { version = "1.0.113" }
serde_test = { version = "1.0.152" }
serde_with = { version = "3.6.0", default-features = false, features = [
"macros",
"macros",
] }
shellexpand = { version = "3.0.0" }
similar = { version = "2.4.0", features = ["inline"] }
@@ -136,7 +142,10 @@ toml = { version = "0.8.11" }
tracing = { version = "0.1.40" }
tracing-flame = { version = "0.2.0" }
tracing-indicatif = { version = "0.3.6" }
tracing-subscriber = { version = "0.3.18", default-features = false, features = ["env-filter", "fmt"] }
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
"env-filter",
"fmt",
] }
tracing-tree = { version = "0.4.0" }
typed-arena = { version = "2.0.2" }
unic-ucd-category = { version = "0.9" }
@@ -147,10 +156,10 @@ unicode-normalization = { version = "0.1.23" }
ureq = { version = "2.9.6" }
url = { version = "2.5.0" }
uuid = { version = "1.6.1", features = [
"v4",
"fast-rng",
"macro-diagnostics",
"js",
"v4",
"fast-rng",
"macro-diagnostics",
"js",
] }
walkdir = { version = "2.3.2" }
wasm-bindgen = { version = "0.2.92" }
@@ -161,7 +170,10 @@ zip = { version = "0.6.6", default-features = false }
[workspace.lints.rust]
unsafe_code = "warn"
unreachable_pub = "warn"
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(fuzzing)", "cfg(codspeed)"] }
unexpected_cfgs = { level = "warn", check-cfg = [
"cfg(fuzzing)",
"cfg(codspeed)",
] }
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -2 }
@@ -244,23 +256,23 @@ windows-archive = ".zip"
unix-archive = ".tar.gz"
# Target platforms to build apps for (Rust target-triple syntax)
targets = [
"aarch64-apple-darwin",
"aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"arm-unknown-linux-musleabihf",
"armv7-unknown-linux-gnueabihf",
"armv7-unknown-linux-musleabihf",
"i686-pc-windows-msvc",
"i686-unknown-linux-gnu",
"i686-unknown-linux-musl",
"powerpc64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"aarch64-apple-darwin",
"aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"arm-unknown-linux-musleabihf",
"armv7-unknown-linux-gnueabihf",
"armv7-unknown-linux-musleabihf",
"i686-pc-windows-msvc",
"i686-unknown-linux-gnu",
"i686-unknown-linux-musl",
"powerpc64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
]
# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true)
auto-includes = false
@@ -279,7 +291,11 @@ local-artifacts-jobs = ["./build-binaries", "./build-docker"]
# Publish jobs to run in CI
publish-jobs = ["./publish-pypi", "./publish-wasm"]
# Post-announce jobs to run in CI
post-announce-jobs = ["./notify-dependents", "./publish-docs", "./publish-playground"]
post-announce-jobs = [
"./notify-dependents",
"./publish-docs",
"./publish-playground",
]
# Custom permissions for GitHub Jobs
github-custom-job-permissions = { "build-docker" = { packages = "write", contents = "read" }, "publish-wasm" = { contents = "read", id-token = "write", packages = "write" } }
# Whether to install an updater program

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.6.6/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.6.6/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.6.9/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.6.9/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -170,7 +170,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.6.6
rev: v0.6.9
hooks:
# Run the linter.
- id: ruff
@@ -182,7 +182,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or with [various other editors](https://docs.astral.sh/ruff/editors/setup).
Ruff can also be used as a [GitHub Action](https://github.com/features/actions) via
[`ruff-action`](https://github.com/chartboost/ruff-action):
[`ruff-action`](https://github.com/astral-sh/ruff-action):
```yaml
name: Ruff
@@ -192,7 +192,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
- uses: astral-sh/ruff-action@v1
```
### Configuration<a id="configuration"></a>

View File

@@ -1,6 +1,6 @@
[files]
# https://github.com/crate-ci/typos/issues/868
extend-exclude = ["crates/red_knot_python_semantic/vendor/**/*", "**/resources/**/*", "**/snapshots/**/*"]
extend-exclude = ["crates/red_knot_vendored/vendor/**/*", "**/resources/**/*", "**/snapshots/**/*"]
[default.extend-words]
"arange" = "arange" # e.g. `numpy.arange`
@@ -8,7 +8,7 @@ hel = "hel"
whos = "whos"
spawnve = "spawnve"
ned = "ned"
pn = "pn" # `import panel as pd` is a thing
pn = "pn" # `import panel as pn` is a thing
poit = "poit"
BA = "BA" # acronym for "Bad Allowed", used in testing.
jod = "jod" # e.g., `jod-thread`

View File

@@ -13,9 +13,8 @@ license.workspace = true
[dependencies]
red_knot_python_semantic = { workspace = true }
red_knot_workspace = { workspace = true }
red_knot_workspace = { workspace = true, features = ["zstd"] }
red_knot_server = { workspace = true }
ruff_db = { workspace = true, features = ["os", "cache"] }
anyhow = { workspace = true }

View File

@@ -160,7 +160,7 @@ fn run() -> anyhow::Result<ExitStatus> {
SystemPathBuf::from_path_buf(cwd)
.map_err(|path| {
anyhow!(
"The current working directory '{}' contains non-unicode characters. Red Knot only supports unicode paths.",
"The current working directory `{}` contains non-Unicode characters. Red Knot only supports Unicode paths.",
path.display()
)
})?
@@ -174,7 +174,7 @@ fn run() -> anyhow::Result<ExitStatus> {
Ok(SystemPath::absolute(cwd, &cli_base_path))
} else {
Err(anyhow!(
"Provided current-directory path '{cwd}' is not a directory."
"Provided current-directory path `{cwd}` is not a directory"
))
}
})

View File

@@ -42,14 +42,14 @@ impl TestCase {
fn stop_watch(&mut self) -> Vec<watch::ChangeEvent> {
self.try_stop_watch(Duration::from_secs(10))
.expect("Expected watch changes but observed none.")
.expect("Expected watch changes but observed none")
}
fn try_stop_watch(&mut self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
let watcher = self
.watcher
.take()
.expect("Cannot call `stop_watch` more than once.");
.expect("Cannot call `stop_watch` more than once");
let mut all_events = self
.changes_receiver
@@ -72,7 +72,7 @@ impl TestCase {
#[cfg(unix)]
fn take_watch_changes(&self) -> Vec<watch::ChangeEvent> {
self.try_take_watch_changes(Duration::from_secs(10))
.expect("Expected watch changes but observed none.")
.expect("Expected watch changes but observed none")
}
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
@@ -150,14 +150,14 @@ where
let absolute_path = workspace_path.join(relative_path);
if let Some(parent) = absolute_path.parent() {
std::fs::create_dir_all(parent).with_context(|| {
format!("Failed to create parent directory for file '{relative_path}'.",)
format!("Failed to create parent directory for file `{relative_path}`")
})?;
}
let mut file = std::fs::File::create(absolute_path.as_std_path())
.with_context(|| format!("Failed to open file '{relative_path}'"))?;
.with_context(|| format!("Failed to open file `{relative_path}`"))?;
file.write_all(content.as_bytes())
.with_context(|| format!("Failed to write to file '{relative_path}'"))?;
.with_context(|| format!("Failed to write to file `{relative_path}`"))?;
file.sync_data()?;
}
@@ -194,7 +194,7 @@ where
let root_path = SystemPath::from_std_path(temp_dir.path()).ok_or_else(|| {
anyhow!(
"Temp directory '{}' is not a valid UTF-8 path.",
"Temporary directory `{}` is not a valid UTF-8 path.",
temp_dir.path().display()
)
})?;
@@ -209,7 +209,7 @@ where
let workspace_path = root_path.join("workspace");
std::fs::create_dir_all(workspace_path.as_std_path())
.with_context(|| format!("Failed to create workspace directory '{workspace_path}'",))?;
.with_context(|| format!("Failed to create workspace directory `{workspace_path}`"))?;
setup_files
.setup(&root_path, &workspace_path)
@@ -233,7 +233,7 @@ where
}))
{
std::fs::create_dir_all(path.as_std_path())
.with_context(|| format!("Failed to create search path '{path}'"))?;
.with_context(|| format!("Failed to create search path `{path}`"))?;
}
let configuration = Configuration {
@@ -501,7 +501,10 @@ fn directory_moved_to_workspace() -> anyhow::Result<()> {
.with_context(|| "Failed to create __init__.py")?;
std::fs::write(a_original_path.as_std_path(), "").with_context(|| "Failed to create a.py")?;
let sub_a_module = resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap());
let sub_a_module = resolve_module(
case.db().upcast(),
&ModuleName::new_static("sub.a").unwrap(),
);
assert_eq!(sub_a_module, None);
assert_eq!(
@@ -525,7 +528,11 @@ fn directory_moved_to_workspace() -> anyhow::Result<()> {
.expect("a.py to exist");
// `import sub.a` should now resolve
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module(
case.db().upcast(),
&ModuleName::new_static("sub.a").unwrap()
)
.is_some());
assert_eq!(
case.collect_package_files(&case.workspace_path("bar.py")),
@@ -544,7 +551,11 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
])?;
let bar = case.system_file(case.workspace_path("bar.py")).unwrap();
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module(
case.db().upcast(),
&ModuleName::new_static("sub.a").unwrap()
)
.is_some());
let sub_path = case.workspace_path("sub");
let init_file = case
@@ -569,7 +580,11 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
case.apply_changes(changes);
// `import sub.a` should no longer resolve
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(resolve_module(
case.db().upcast(),
&ModuleName::new_static("sub.a").unwrap()
)
.is_none());
assert!(!init_file.exists(case.db()));
assert!(!a_file.exists(case.db()));
@@ -592,10 +607,14 @@ fn directory_renamed() -> anyhow::Result<()> {
let bar = case.system_file(case.workspace_path("bar.py")).unwrap();
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module(
case.db().upcast(),
ModuleName::new_static("foo.baz").unwrap()
&ModuleName::new_static("sub.a").unwrap()
)
.is_some());
assert!(resolve_module(
case.db().upcast(),
&ModuleName::new_static("foo.baz").unwrap()
)
.is_none());
@@ -623,11 +642,15 @@ fn directory_renamed() -> anyhow::Result<()> {
case.apply_changes(changes);
// `import sub.a` should no longer resolve
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(resolve_module(
case.db().upcast(),
&ModuleName::new_static("sub.a").unwrap()
)
.is_none());
// `import foo.baz` should now resolve
assert!(resolve_module(
case.db().upcast(),
ModuleName::new_static("foo.baz").unwrap()
&ModuleName::new_static("foo.baz").unwrap()
)
.is_some());
@@ -665,7 +688,11 @@ fn directory_deleted() -> anyhow::Result<()> {
let bar = case.system_file(case.workspace_path("bar.py")).unwrap();
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_some(),);
assert!(resolve_module(
case.db().upcast(),
&ModuleName::new_static("sub.a").unwrap()
)
.is_some());
let sub_path = case.workspace_path("sub");
@@ -688,7 +715,11 @@ fn directory_deleted() -> anyhow::Result<()> {
case.apply_changes(changes);
// `import sub.a` should no longer resolve
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(resolve_module(
case.db().upcast(),
&ModuleName::new_static("sub.a").unwrap()
)
.is_none());
assert!(!init_file.exists(case.db()));
assert!(!a_file.exists(case.db()));
@@ -710,7 +741,7 @@ fn search_path() -> anyhow::Result<()> {
let site_packages = case.root_path().join("site_packages");
assert_eq!(
resolve_module(case.db(), ModuleName::new("a").unwrap()),
resolve_module(case.db(), &ModuleName::new("a").unwrap()),
None
);
@@ -720,7 +751,7 @@ fn search_path() -> anyhow::Result<()> {
case.apply_changes(changes);
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("a").unwrap()).is_some());
assert!(resolve_module(case.db().upcast(), &ModuleName::new_static("a").unwrap()).is_some());
assert_eq!(
case.collect_package_files(&case.workspace_path("bar.py")),
&[case.system_file(case.workspace_path("bar.py")).unwrap()]
@@ -736,7 +767,7 @@ fn add_search_path() -> anyhow::Result<()> {
let site_packages = case.workspace_path("site_packages");
std::fs::create_dir_all(site_packages.as_std_path())?;
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("a").unwrap()).is_none());
assert!(resolve_module(case.db().upcast(), &ModuleName::new_static("a").unwrap()).is_none());
// Register site-packages as a search path.
case.update_search_path_settings(SearchPathConfiguration {
@@ -751,7 +782,7 @@ fn add_search_path() -> anyhow::Result<()> {
case.apply_changes(changes);
assert!(resolve_module(case.db().upcast(), ModuleName::new_static("a").unwrap()).is_some());
assert!(resolve_module(case.db().upcast(), &ModuleName::new_static("a").unwrap()).is_some());
Ok(())
}
@@ -805,7 +836,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
// Unset the custom typeshed directory.
assert_eq!(
resolve_module(case.db(), ModuleName::new("os").unwrap()),
resolve_module(case.db(), &ModuleName::new("os").unwrap()),
None
);
@@ -820,7 +851,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
case.apply_changes(changes);
assert!(resolve_module(case.db(), ModuleName::new("os").unwrap()).is_some());
assert!(resolve_module(case.db(), &ModuleName::new("os").unwrap()).is_some());
Ok(())
}
@@ -1044,7 +1075,7 @@ mod unix {
let baz = resolve_module(
case.db().upcast(),
ModuleName::new_static("bar.baz").unwrap(),
&ModuleName::new_static("bar.baz").unwrap(),
)
.expect("Expected bar.baz to exist in site-packages.");
let baz_workspace = case.workspace_path("bar/baz.py");
@@ -1125,7 +1156,7 @@ mod unix {
let baz = resolve_module(
case.db().upcast(),
ModuleName::new_static("bar.baz").unwrap(),
&ModuleName::new_static("bar.baz").unwrap(),
)
.expect("Expected bar.baz to exist in site-packages.");
let bar_baz = case.workspace_path("bar/baz.py");
@@ -1229,7 +1260,7 @@ mod unix {
let baz = resolve_module(
case.db().upcast(),
ModuleName::new_static("bar.baz").unwrap(),
&ModuleName::new_static("bar.baz").unwrap(),
)
.expect("Expected bar.baz to exist in site-packages.");
let baz_site_packages_path =

View File

@@ -24,7 +24,7 @@ bitflags = { workspace = true }
camino = { workspace = true }
compact_str = { workspace = true }
countme = { workspace = true }
once_cell = { workspace = true }
itertools = { workspace = true}
ordermap = { workspace = true }
salsa = { workspace = true }
thiserror = { workspace = true }
@@ -34,26 +34,18 @@ hashbrown = { workspace = true }
smallvec = { workspace = true }
static_assertions = { workspace = true }
test-case = { workspace = true }
[build-dependencies]
path-slash = { workspace = true }
walkdir = { workspace = true }
[target.'cfg(not(target_arch = "powerpc64"))'.build-dependencies]
zip = { workspace = true, features = ["deflate", "zstd"] }
[target.'cfg(target_arch = "powerpc64")'.build-dependencies]
zip = { workspace = true, features = ["deflate"] }
memchr = { workspace = true }
[dev-dependencies]
ruff_db = { workspace = true, features = ["os", "testing"] }
ruff_python_parser = { workspace = true }
red_knot_test = { workspace = true }
red_knot_vendored = { workspace = true }
anyhow = { workspace = true }
insta = { workspace = true }
rstest = { workspace = true }
tempfile = { workspace = true }
walkdir = { workspace = true }
zip = { workspace = true }
[lints]
workspace = true

View File

@@ -1,94 +1,4 @@
//! Build script to package our vendored typeshed files
//! into a zip archive that can be included in the Ruff binary.
//!
//! This script should be automatically run at build time
//! whenever the script itself changes, or whenever any files
//! in `crates/red_knot_python_semantic/vendor/typeshed` change.
use std::fs::File;
use std::path::Path;
use path_slash::PathExt;
use zip::result::ZipResult;
use zip::write::{FileOptions, ZipWriter};
use zip::CompressionMethod;
const TYPESHED_SOURCE_DIR: &str = "vendor/typeshed";
const TYPESHED_ZIP_LOCATION: &str = "/zipped_typeshed.zip";
/// Recursively zip the contents of an entire directory.
///
/// This routine is adapted from a recipe at
/// <https://github.com/zip-rs/zip-old/blob/5d0f198124946b7be4e5969719a7f29f363118cd/examples/write_dir.rs>
fn zip_dir(directory_path: &str, writer: File) -> ZipResult<File> {
let mut zip = ZipWriter::new(writer);
// Use deflated compression for WASM builds because compiling `zstd-sys` requires clang
// [source](https://github.com/gyscos/zstd-rs/wiki/Compile-for-WASM) which complicates the build
// by a lot. Deflated compression is slower but it shouldn't matter much for the WASM use case
// (WASM itself is already slower than a native build for a specific platform).
// We can't use `#[cfg(...)]` here because the target-arch in a build script is the
// architecture of the system running the build script and not the architecture of the build-target.
// That's why we use the `TARGET` environment variable here.
#[cfg(target_arch = "powerpc64")]
let method = CompressionMethod::Deflated;
#[cfg(not(target_arch = "powerpc64"))]
let method = {
let target = std::env::var("TARGET").unwrap();
if target.contains("wasm32") || target.contains("powerpc64") {
CompressionMethod::Deflated
} else {
CompressionMethod::Zstd
}
};
let options = FileOptions::default()
.compression_method(method)
.unix_permissions(0o644);
for entry in walkdir::WalkDir::new(directory_path) {
let dir_entry = entry.unwrap();
let absolute_path = dir_entry.path();
let normalized_relative_path = absolute_path
.strip_prefix(Path::new(directory_path))
.unwrap()
.to_slash()
.expect("Unexpected non-utf8 typeshed path!");
// Write file or directory explicitly
// Some unzip tools unzip files with directory paths correctly, some do not!
if absolute_path.is_file() {
println!("adding file {absolute_path:?} as {normalized_relative_path:?} ...");
zip.start_file(normalized_relative_path, options)?;
let mut f = File::open(absolute_path)?;
std::io::copy(&mut f, &mut zip).unwrap();
} else if !normalized_relative_path.is_empty() {
// Only if not root! Avoids path spec / warning
// and mapname conversion failed error on unzip
println!("adding dir {absolute_path:?} as {normalized_relative_path:?} ...");
zip.add_directory(normalized_relative_path, options)?;
}
}
zip.finish()
}
fn main() {
println!("cargo:rerun-if-changed={TYPESHED_SOURCE_DIR}");
assert!(
Path::new(TYPESHED_SOURCE_DIR).is_dir(),
"Where is typeshed?"
);
let out_dir = std::env::var("OUT_DIR").unwrap();
// N.B. Deliberately using `format!()` instead of `Path::join()` here,
// so that we use `/` as a path separator on all platforms.
// That enables us to load the typeshed zip at compile time in `module.rs`
// (otherwise we'd have to dynamically determine the exact path to the typeshed zip
// based on the default path separator for the specific platform we're on,
// which can't be done at compile time.)
let zipped_typeshed_location = format!("{out_dir}{TYPESHED_ZIP_LOCATION}");
let zipped_typeshed = File::create(zipped_typeshed_location).unwrap();
zip_dir(TYPESHED_SOURCE_DIR, zipped_typeshed).unwrap();
/// Rebuild the crate if a test file is added or removed from
pub fn main() {
println!("cargo:rerun-if-changed=resources/mdtest");
}

View File

@@ -0,0 +1,4 @@
Markdown files within the `mdtest/` subdirectory are tests of type inference and type checking;
executed by the `tests/mdtest.rs` integration test.
See `crates/red_knot_test/README.md` for documentation of this test format.

View File

@@ -0,0 +1,25 @@
# Assignment with annotations
## Annotation only transparent to local inference
```py
x = 1
x: int
y = x
reveal_type(y) # revealed: Literal[1]
```
## Violates own annotation
```py
x: int = 'foo' # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`"
```
## Violates previous annotation
```py
x: int
x = 'foo' # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`"
```

View File

@@ -0,0 +1,9 @@
# Multi-target assignment
## Basic
```py
x = y = 1
reveal_type(x) # revealed: Literal[1]
reveal_type(y) # revealed: Literal[1]
```

View File

@@ -0,0 +1,32 @@
# Unbound
## Maybe unbound
```py
if flag:
y = 3
x = y
reveal_type(x) # revealed: Unbound | Literal[3]
```
## Unbound
```py
x = foo; foo = 1
reveal_type(x) # revealed: Unbound
```
## Unbound class variable
Class variables can reference global variables unless overridden within the class scope.
```py
x = 1
class C:
y = x
if flag:
x = 2
reveal_type(C.x) # revealed: Unbound | Literal[2]
reveal_type(C.y) # revealed: Literal[1]
```

View File

@@ -0,0 +1,17 @@
# Walrus operator
## Basic
```py
x = (y := 1) + 1
reveal_type(x) # revealed: Literal[2]
reveal_type(y) # revealed: Literal[1]
```
## Walrus self-addition
```py
x = 0
(x := x + 1)
reveal_type(x) # revealed: Literal[1]
```

View File

@@ -0,0 +1,15 @@
# Class attributes
## Union of attributes
```py
if flag:
class C:
x = 1
else:
class C:
x = 2
y = C.x
reveal_type(y) # revealed: Literal[1, 2]
```

View File

@@ -0,0 +1,36 @@
## Binary operations on integers
## Basic Arithmetic
```py
a = 2 + 1
b = a - 4
c = a * b
d = c // 3
e = c / 3
f = 5 % 3
reveal_type(a) # revealed: Literal[3]
reveal_type(b) # revealed: Literal[-1]
reveal_type(c) # revealed: Literal[-3]
reveal_type(d) # revealed: Literal[-1]
reveal_type(e) # revealed: float
reveal_type(f) # revealed: Literal[2]
```
## Division by Zero
```py
# TODO: `a` should be `int` and `e` should be `float` once we support inference.
a = 1 / 0 # error: "Cannot divide object of type `Literal[1]` by zero"
b = 2 // 0 # error: "Cannot floor divide object of type `Literal[2]` by zero"
c = 3 % 0 # error: "Cannot reduce object of type `Literal[3]` modulo zero"
d = int() / 0 # error: "Cannot divide object of type `int` by zero"
e = 1.0 / 0 # error: "Cannot divide object of type `float` by zero"
reveal_type(a) # revealed: float
reveal_type(b) # revealed: int
reveal_type(c) # revealed: int
reveal_type(d) # revealed: @Todo
reveal_type(e) # revealed: @Todo
```

View File

@@ -0,0 +1,21 @@
# Callable instance
## Dunder call
```py
class Multiplier:
def __init__(self, factor: float):
self.factor = factor
def __call__(self, number: float) -> float:
return number * self.factor
a = Multiplier(2.0)(3.0)
class Unit: ...
b = Unit()(3.0) # error: "Object of type `Unit` is not callable"
reveal_type(a) # revealed: float
reveal_type(b) # revealed: Unknown
```

View File

@@ -0,0 +1,8 @@
# Constructor
```py
class Foo: ...
x = Foo()
reveal_type(x) # revealed: Foo
```

View File

@@ -0,0 +1,51 @@
# Call expression
## Simple
```py
def get_int() -> int:
return 42
x = get_int()
reveal_type(x) # revealed: int
```
## Async
```py
async def get_int_async() -> int:
return 42
x = get_int_async()
# TODO: we don't yet support `types.CoroutineType`, should be generic `Coroutine[Any, Any, int]`
reveal_type(x) # revealed: @Todo
```
## Decorated
```py
from typing import Callable
def foo() -> int:
return 42
def decorator(func) -> Callable[[], int]:
return foo
@decorator
def bar() -> str:
return 'bar'
x = bar()
# TODO: should reveal `int`, as the decorator replaces `bar` with `foo`
reveal_type(x) # revealed: @Todo
```
## Invalid callable
```py
nonsense = 123
x = nonsense() # error: "Object of type `Literal[123]` is not callable"
```

View File

@@ -0,0 +1,74 @@
# Unions in calls
## Union of return types
```py
if flag:
def f() -> int:
return 1
else:
def f() -> str:
return 'foo'
x = f()
reveal_type(x) # revealed: int | str
```
## Calling with an unknown union
```py
from nonexistent import f # error: [unresolved-import] "Cannot resolve import `nonexistent`"
if flag:
def f() -> int:
return 1
x = f()
reveal_type(x) # revealed: Unknown | int
```
## Non-callable elements in a union
Calling a union with a non-callable element should emit a diagnostic.
```py
if flag:
f = 1
else:
def f() -> int:
return 1
x = f() # error: "Object of type `Literal[1] | Literal[f]` is not callable (due to union element `Literal[1]`)"
reveal_type(x) # revealed: Unknown | int
```
## Multiple non-callable elements in a union
Calling a union with multiple non-callable elements should mention all of them in the diagnostic.
```py
if flag:
f = 1
elif flag2:
f = 'foo'
else:
def f() -> int:
return 1
x = f() # error: "Object of type `Literal[1] | Literal["foo"] | Literal[f]` is not callable (due to union elements Literal[1], Literal["foo"])"
reveal_type(x) # revealed: Unknown | int
```
## All non-callable union elements
Calling a union with no callable elements can emit a simpler diagnostic.
```py
if flag:
f = 1
else:
f = 'foo'
x = f() # error: "Object of type `Literal[1] | Literal["foo"]` is not callable"
reveal_type(x) # revealed: Unknown
```

View File

@@ -0,0 +1,43 @@
### Comparison: Byte literals
These tests assert that we infer precise `Literal` types for comparisons between objects
inferred as having `Literal` bytes types:
```py
reveal_type(b"abc" == b"abc") # revealed: Literal[True]
reveal_type(b"abc" == b"ab") # revealed: Literal[False]
reveal_type(b"abc" != b"abc") # revealed: Literal[False]
reveal_type(b"abc" != b"ab") # revealed: Literal[True]
reveal_type(b"abc" < b"abd") # revealed: Literal[True]
reveal_type(b"abc" < b"abb") # revealed: Literal[False]
reveal_type(b"abc" <= b"abc") # revealed: Literal[True]
reveal_type(b"abc" <= b"abb") # revealed: Literal[False]
reveal_type(b"abc" > b"abd") # revealed: Literal[False]
reveal_type(b"abc" > b"abb") # revealed: Literal[True]
reveal_type(b"abc" >= b"abc") # revealed: Literal[True]
reveal_type(b"abc" >= b"abd") # revealed: Literal[False]
reveal_type(b"" in b"") # revealed: Literal[True]
reveal_type(b"" in b"abc") # revealed: Literal[True]
reveal_type(b"abc" in b"") # revealed: Literal[False]
reveal_type(b"ab" in b"abc") # revealed: Literal[True]
reveal_type(b"abc" in b"abc") # revealed: Literal[True]
reveal_type(b"d" in b"abc") # revealed: Literal[False]
reveal_type(b"ac" in b"abc") # revealed: Literal[False]
reveal_type(b"\x81\x82" in b"\x80\x81\x82") # revealed: Literal[True]
reveal_type(b"\x82\x83" in b"\x80\x81\x82") # revealed: Literal[False]
reveal_type(b"ab" not in b"abc") # revealed: Literal[False]
reveal_type(b"ac" not in b"abc") # revealed: Literal[True]
reveal_type(b"abc" is b"abc") # revealed: bool
reveal_type(b"abc" is b"ab") # revealed: Literal[False]
reveal_type(b"abc" is not b"abc") # revealed: bool
reveal_type(b"abc" is not b"ab") # revealed: Literal[True]
```

View File

@@ -0,0 +1,41 @@
# Comparing integers
## Integer literals
```py
a = 1 == 1 == True
b = 1 == 1 == 2 == 4
c = False < True <= 2 < 3 != 6
d = 1 < 1
e = 1 > 1
f = 1 is 1
g = 1 is not 1
h = 1 is 2
i = 1 is not 7
j = 1 <= "" and 0 < 1
reveal_type(a) # revealed: Literal[True]
reveal_type(b) # revealed: Literal[False]
reveal_type(c) # revealed: Literal[True]
reveal_type(d) # revealed: Literal[False]
reveal_type(e) # revealed: Literal[False]
reveal_type(f) # revealed: bool
reveal_type(g) # revealed: bool
reveal_type(h) # revealed: Literal[False]
reveal_type(i) # revealed: Literal[True]
reveal_type(j) # revealed: @Todo | Literal[True]
```
## Integer instance
```py
# TODO: implement lookup of `__eq__` on typeshed `int` stub.
def int_instance() -> int: ...
a = 1 == int_instance()
b = 9 < int_instance()
c = int_instance() < int_instance()
reveal_type(a) # revealed: @Todo
reveal_type(b) # revealed: bool
reveal_type(c) # revealed: bool
```

View File

@@ -0,0 +1,37 @@
# Non boolean returns
Walking through examples:
- `a = A() < B() < C()`
1. `A() < B() and B() < C()` - split in N comparison
1. `A()` and `B()` - evaluate outcome types
1. `bool` and `bool` - evaluate truthiness
1. `A | B` - union of "first true" types
- `b = 0 < 1 < A() < 3`
1. `0 < 1 and 1 < A() and A() < 3` - split in N comparison
1. `True` and `bool` and `A` - evaluate outcome types
1. `True` and `bool` and `bool` - evaluate truthiness
1. `bool | A` - union of "true" types
- `c = 10 < 0 < A() < B() < C()` short-circuit to False
```py
from __future__ import annotations
class A:
def __lt__(self, other) -> A: ...
class B:
def __lt__(self, other) -> B: ...
class C:
def __lt__(self, other) -> C: ...
a = A() < B() < C()
b = 0 < 1 < A() < 3
c = 10 < 0 < A() < B() < C()
reveal_type(a) # revealed: A | B
reveal_type(b) # revealed: bool | A
reveal_type(c) # revealed: Literal[False]
```

View File

@@ -0,0 +1,29 @@
# Comparing strings
## String literals
```py
def str_instance() -> str: ...
a = "abc" == "abc"
b = "ab_cd" <= "ab_ce"
c = "abc" in "ab cd"
d = "" not in "hello"
e = "--" is "--"
f = "A" is "B"
g = "--" is not "--"
h = "A" is not "B"
i = str_instance() < "..."
# ensure we're not comparing the interned salsa symbols, which compare by order of declaration.
j = "ab" < "ab_cd"
reveal_type(a) # revealed: Literal[True]
reveal_type(b) # revealed: Literal[True]
reveal_type(c) # revealed: Literal[False]
reveal_type(d) # revealed: Literal[False]
reveal_type(e) # revealed: bool
reveal_type(f) # revealed: Literal[False]
reveal_type(g) # revealed: bool
reveal_type(h) # revealed: Literal[True]
reveal_type(i) # revealed: bool
reveal_type(j) # revealed: Literal[True]
```

View File

@@ -0,0 +1,15 @@
# Unsupported operators
```py
a = 1 in 7 # error: "Operator `in` is not supported for types `Literal[1]` and `Literal[7]`"
b = 0 not in 10 # error: "Operator `not in` is not supported for types `Literal[0]` and `Literal[10]`"
c = object() < 5 # error: "Operator `<` is not supported for types `object` and `Literal[5]`"
# TODO should error, need to check if __lt__ signature is valid for right operand
d = 5 < object()
reveal_type(a) # revealed: bool
reveal_type(b) # revealed: bool
reveal_type(c) # revealed: Unknown
# TODO: should be `Unknown`
reveal_type(d) # revealed: bool
```

View File

@@ -0,0 +1,35 @@
# If expressions
## Simple if-expression
```py
x = 1 if flag else 2
reveal_type(x) # revealed: Literal[1, 2]
```
## If-expression with walrus operator
```py
y = 0
z = 0
x = (y := 1) if flag else (z := 2)
a = y
b = z
reveal_type(x) # revealed: Literal[1, 2]
reveal_type(a) # revealed: Literal[0, 1]
reveal_type(b) # revealed: Literal[0, 2]
```
## Nested if-expression
```py
x = 1 if flag else 2 if flag2 else 3
reveal_type(x) # revealed: Literal[1, 2, 3]
```
## None
```py
x = 1 if flag else None
reveal_type(x) # revealed: Literal[1] | None
```

View File

@@ -0,0 +1,97 @@
# If statements
## Simple if
```py
y = 1
y = 2
if flag:
y = 3
x = y
reveal_type(x) # revealed: Literal[2, 3]
```
## Simple if-elif-else
```py
y = 1
y = 2
if flag:
y = 3
elif flag2:
y = 4
else:
r = y
y = 5
s = y
x = y
reveal_type(x) # revealed: Literal[3, 4, 5]
reveal_type(r) # revealed: Unbound | Literal[2]
reveal_type(s) # revealed: Unbound | Literal[5]
```
## Single symbol across if-elif-else
```py
if flag:
y = 1
elif flag2:
y = 2
else:
y = 3
reveal_type(y) # revealed: Literal[1, 2, 3]
```
## if-elif-else without else assignment
```py
y = 0
if flag:
y = 1
elif flag2:
y = 2
else:
pass
reveal_type(y) # revealed: Literal[0, 1, 2]
```
## if-elif-else with intervening assignment
```py
y = 0
if flag:
y = 1
z = 3
elif flag2:
y = 2
else:
pass
reveal_type(y) # revealed: Literal[0, 1, 2]
```
## Nested if statement
```py
y = 0
if flag:
if flag2:
y = 1
reveal_type(y) # revealed: Literal[0, 1]
```
## if-elif without else
```py
y = 1
y = 2
if flag:
y = 3
elif flag2:
y = 4
x = y
reveal_type(x) # revealed: Literal[2, 3, 4]
```

View File

@@ -0,0 +1,39 @@
# Pattern matching
## With wildcard
```py
match 0:
case 1:
y = 2
case _:
y = 3
reveal_type(y) # revealed: Literal[2, 3]
```
## Without wildcard
```py
match 0:
case 1:
y = 2
case 2:
y = 3
reveal_type(y) # revealed: Unbound | Literal[2, 3]
```
## Basic match
```py
y = 1
y = 2
match 0:
case 1:
y = 3
case 2:
y = 4
reveal_type(y) # revealed: Literal[2, 3, 4]
```

View File

@@ -0,0 +1,39 @@
# Errors while declaring
## Violates previous assignment
```py
x = 1
x: str # error: [invalid-declaration] "Cannot declare type `str` for inferred type `Literal[1]`"
```
## Incompatible declarations
```py
if flag:
x: str
else:
x: int
x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: str, int"
```
## Partial declarations
```py
if flag:
x: int
x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: Unknown, int"
```
## Incompatible declarations with bad assignment
```py
if flag:
x: str
else:
x: int
# error: [conflicting-declarations]
# error: [invalid-assignment]
x = b'foo'
```

View File

@@ -0,0 +1,55 @@
# Exception Handling
## Single Exception
```py
import re
try:
x
except NameError as e:
reveal_type(e) # revealed: NameError
except re.error as f:
reveal_type(f) # revealed: error
```
## Unknown type in except handler does not cause spurious diagnostic
```py
from nonexistent_module import foo # error: [unresolved-import]
try:
x
except foo as e:
reveal_type(foo) # revealed: Unknown
reveal_type(e) # revealed: Unknown
```
## Multiple Exceptions in a Tuple
```py
EXCEPTIONS = (AttributeError, TypeError)
try:
x
except (RuntimeError, OSError) as e:
reveal_type(e) # revealed: RuntimeError | OSError
except EXCEPTIONS as f:
reveal_type(f) # revealed: AttributeError | TypeError
```
## Dynamic exception types
```py
def foo(x: type[AttributeError], y: tuple[type[OSError], type[RuntimeError]], z: tuple[type[BaseException], ...]):
try:
w
except x as e:
# TODO: should be `AttributeError`
reveal_type(e) # revealed: @Todo
except y as f:
# TODO: should be `OSError | RuntimeError`
reveal_type(f) # revealed: @Todo
except z as g:
# TODO: should be `BaseException`
reveal_type(g) # revealed: @Todo
```

View File

@@ -0,0 +1,32 @@
# Except star
TODO(Alex): Once we support `sys.version_info` branches, we can set `--target-version=py311` in these tests and the inferred type will just be `BaseExceptionGroup`
## Except\* with BaseException
```py
try:
x
except* BaseException as e:
reveal_type(e) # revealed: Unknown | BaseExceptionGroup
```
## Except\* with specific exception
```py
try:
x
except* OSError as e:
# TODO(Alex): more precise would be `ExceptionGroup[OSError]`
reveal_type(e) # revealed: Unknown | BaseExceptionGroup
```
## Except\* with multiple exceptions
```py
try:
x
except* (TypeError, AttributeError) as e:
#TODO(Alex): more precise would be `ExceptionGroup[TypeError | AttributeError]`.
reveal_type(e) # revealed: Unknown | BaseExceptionGroup
```

View File

@@ -0,0 +1,151 @@
# Expressions
## OR
```py
def foo() -> str:
pass
a = True or False
b = 'x' or 'y' or 'z'
c = '' or 'y' or 'z'
d = False or 'z'
e = False or True
f = False or False
g = foo() or False
h = foo() or True
reveal_type(a) # revealed: Literal[True]
reveal_type(b) # revealed: Literal["x"]
reveal_type(c) # revealed: Literal["y"]
reveal_type(d) # revealed: Literal["z"]
reveal_type(e) # revealed: Literal[True]
reveal_type(f) # revealed: Literal[False]
reveal_type(g) # revealed: str | Literal[False]
reveal_type(h) # revealed: str | Literal[True]
```
## AND
```py
def foo() -> str:
pass
a = True and False
b = False and True
c = foo() and False
d = foo() and True
e = 'x' and 'y' and 'z'
f = 'x' and 'y' and ''
g = '' and 'y'
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: Literal[False]
reveal_type(c) # revealed: str | Literal[False]
reveal_type(d) # revealed: str | Literal[True]
reveal_type(e) # revealed: Literal["z"]
reveal_type(f) # revealed: Literal[""]
reveal_type(g) # revealed: Literal[""]
```
## Simple function calls to bool
```py
def returns_bool() -> bool:
return True
if returns_bool():
x = True
else:
x = False
reveal_type(x) # revealed: bool
```
## Complex
```py
def foo() -> str:
pass
a = "x" and "y" or "z"
b = "x" or "y" and "z"
c = "" and "y" or "z"
d = "" or "y" and "z"
e = "x" and "y" or ""
f = "x" or "y" and ""
reveal_type(a) # revealed: Literal["y"]
reveal_type(b) # revealed: Literal["x"]
reveal_type(c) # revealed: Literal["z"]
reveal_type(d) # revealed: Literal["z"]
reveal_type(e) # revealed: Literal["y"]
reveal_type(f) # revealed: Literal["x"]
```
## `bool()` function
## Evaluates to builtin
```py path=a.py
redefined_builtin_bool = bool
def my_bool(x)-> bool: pass
```
```py
from a import redefined_builtin_bool, my_bool
a = redefined_builtin_bool(0)
b = my_bool(0)
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: bool
```
## Truthy values
```py
a = bool(1)
b = bool((0,))
c = bool("NON EMPTY")
d = bool(True)
def foo(): pass
e = bool(foo)
reveal_type(a) # revealed: Literal[True]
reveal_type(b) # revealed: Literal[True]
reveal_type(c) # revealed: Literal[True]
reveal_type(d) # revealed: Literal[True]
reveal_type(e) # revealed: Literal[True]
```
## Falsy values
```py
a = bool(0)
b = bool(())
c = bool(None)
d = bool("")
e = bool(False)
f = bool()
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: Literal[False]
reveal_type(c) # revealed: Literal[False]
reveal_type(d) # revealed: Literal[False]
reveal_type(e) # revealed: Literal[False]
reveal_type(f) # revealed: Literal[False]
```
## Ambiguous values
```py
a = bool([])
b = bool({})
c = bool(set())
reveal_type(a) # revealed: bool
reveal_type(b) # revealed: bool
reveal_type(c) # revealed: bool
```

View File

@@ -0,0 +1,23 @@
# Structures
## Class import following
```py
from b import C as D; E = D
reveal_type(E) # revealed: Literal[C]
```
```py path=b.py
class C: pass
```
## Module member resolution
```py
import b; D = b.C
reveal_type(D) # revealed: Literal[C]
```
```py path=b.py
class C: pass
```

View File

@@ -0,0 +1,6 @@
# Importing builtin module
```py
import builtins; x = builtins.copyright
reveal_type(x) # revealed: Literal[copyright]
```

View File

@@ -0,0 +1,42 @@
# Conditional imports
## Reimport
```py path=c.py
def f(): ...
```
```py path=b.py
if flag:
from c import f
else:
def f(): ...
```
```py
# TODO we should not emit this error
from b import f # error: [invalid-assignment] "Object of type `Literal[f, f]` is not assignable to `Literal[f, f]`"
# TODO: We should disambiguate in such cases, showing `Literal[b.f, c.f]`.
reveal_type(f) # revealed: Literal[f, f]
```
## Reimport with stub declaration
When we have a declared type in one path and only an inferred-from-definition type in the other, we
should still be able to unify those:
```py path=c.pyi
x: int
```
```py path=b.py
if flag:
from c import x
else:
x = 1
```
```py
from b import x
reveal_type(x) # revealed: int
```

View File

@@ -0,0 +1,47 @@
# Unresolved Imports
## Unresolved import statement
```py
import bar # error: "Cannot resolve import `bar`"
```
## Unresolved import from statement
```py
from bar import baz # error: "Cannot resolve import `bar`"
```
## Unresolved import from resolved module
```py path=a.py
```
```py
from a import thing # error: "Module `a` has no member `thing`"
```
## Resolved import of symbol from unresolved import
```py path=a.py
import foo as foo # error: "Cannot resolve import `foo`"
```
Importing the unresolved import into a second file should not trigger an additional "unresolved
import" violation:
```py
from a import foo
```
## No implicit shadowing error
```py path=b.py
x: int
```
```py
from b import x
x = 'foo' # error: "Object of type `Literal["foo"]"
```

View File

@@ -0,0 +1,133 @@
# Relative
## Non-existent
```py path=package/__init__.py
```
```py path=package/bar.py
from .foo import X # error: [unresolved-import]
reveal_type(X) # revealed: Unknown
```
## Simple
```py path=package/__init__.py
```
```py path=package/foo.py
X = 42
```
```py path=package/bar.py
from .foo import X
reveal_type(X) # revealed: Literal[42]
```
## Dotted
```py path=package/__init__.py
```
```py path=package/foo/bar/baz.py
X = 42
```
```py path=package/bar.py
from .foo.bar.baz import X
reveal_type(X) # revealed: Literal[42]
```
## Bare to package
```py path=package/__init__.py
X = 42
```
```py path=package/bar.py
from . import X
reveal_type(X) # revealed: Literal[42]
```
## Non-existent + bare to package
```py path=package/bar.py
from . import X # error: [unresolved-import]
reveal_type(X) # revealed: Unknown
```
## Dunder init
```py path=package/__init__.py
from .foo import X
reveal_type(X) # revealed: Literal[42]
```
```py path=package/foo.py
X = 42
```
## Non-existent + dunder init
```py path=package/__init__.py
from .foo import X # error: [unresolved-import]
reveal_type(X) # revealed: Unknown
```
## Long relative import
```py path=package/__init__.py
```
```py path=package/foo.py
X = 42
```
```py path=package/subpackage/subsubpackage/bar.py
from ...foo import X
reveal_type(X) # revealed: Literal[42]
```
## Unbound symbol
```py path=package/__init__.py
```
```py path=package/foo.py
x
```
```py path=package/bar.py
from .foo import x # error: [unresolved-import]
reveal_type(x) # revealed: Unknown
```
## Bare to module
```py path=package/__init__.py
```
```py path=package/foo.py
X = 42
```
```py path=package/bar.py
# TODO: support submodule imports
from . import foo # error: [unresolved-import]
y = foo.X
# TODO: should be `Literal[42]`
reveal_type(y) # revealed: Unknown
```
## Non-existent + bare to module
```py path=package/__init__.py
```
```py path=package/bar.py
# TODO: submodule imports possibly not supported right now?
from . import foo # error: [unresolved-import]
reveal_type(foo) # revealed: Unknown
```

View File

@@ -0,0 +1,25 @@
# Stubs
## Import from stub declaration
```py
from b import x
y = x
reveal_type(y) # revealed: int
```
```py path=b.pyi
x: int
```
## Import from non-stub with declaration and definition
```py
from b import x
y = x
reveal_type(y) # revealed: int
```
```py path=b.py
x: int = 1
```

View File

@@ -0,0 +1,8 @@
# Boolean literals
```py
x = True
y = False
reveal_type(x) # revealed: Literal[True]
reveal_type(y) # revealed: Literal[False]
```

View File

@@ -0,0 +1,8 @@
# Dictionaries
## Empty dictionary
```py
x = {}
reveal_type(x) # revealed: dict
```

View File

@@ -0,0 +1,8 @@
# Lists
## Empty list
```py
x = []
reveal_type(x) # revealed: list
```

View File

@@ -0,0 +1,8 @@
# Sets
## Basic set
```py
x = {1, 2}
reveal_type(x) # revealed: set
```

View File

@@ -0,0 +1,20 @@
# Tuples
## Empty tuple
```py
x = ()
reveal_type(x) # revealed: tuple[()]
```
## Heterogeneous tuple
```py
x = (1, 'a')
y = (1, (2, 3))
z = (x, 2)
reveal_type(x) # revealed: tuple[Literal[1], Literal["a"]]
reveal_type(y) # revealed: tuple[Literal[1], tuple[Literal[2], Literal[3]]]
reveal_type(z) # revealed: tuple[tuple[Literal[1], Literal["a"]], Literal[2]]
```

View File

@@ -0,0 +1,7 @@
# Complex literals
## Complex numbers
```py
reveal_type(2j) # revealed: complex
```

View File

@@ -0,0 +1,44 @@
# f-strings
## Expression
```py
x = 0
y = str()
z = False
a = f'hello'
b = f'h {x}'
c = 'one ' f'single ' f'literal'
d = 'first ' f'second({b})' f' third'
e = f'-{y}-'
f = f'-{y}-' f'--' '--'
g = f'{z} == {False} is {True}'
reveal_type(a) # revealed: Literal["hello"]
reveal_type(b) # revealed: Literal["h 0"]
reveal_type(c) # revealed: Literal["one single literal"]
reveal_type(d) # revealed: Literal["first second(h 0) third"]
reveal_type(e) # revealed: str
reveal_type(f) # revealed: str
reveal_type(g) # revealed: Literal["False == False is True"]
```
## Conversion Flags
```py
string = 'hello'
a = f'{string!r}'
# TODO: should be `Literal["'hello'"]`
reveal_type(a) # revealed: str
```
## Format Specifiers
```py
a = f'{1:02}'
# TODO: should be `Literal["01"]`
reveal_type(a) # revealed: str
```

View File

@@ -0,0 +1,7 @@
# Float literals
## Basic
```py
reveal_type(1.0) # revealed: float
```

View File

@@ -0,0 +1,56 @@
# Integer literals
## Literals
We can infer an integer literal type:
```py
reveal_type(1) # revealed: Literal[1]
```
## Variable
```py
x = 1
reveal_type(x) # revealed: Literal[1]
```
## Overflow
We only track integer literals within the range of an i64:
```py
reveal_type(9223372036854775808) # revealed: int
```
## Big int
We don't support big integer literals; we just infer `int` type instead:
```py
x = 10_000_000_000_000_000_000
reveal_type(x) # revealed: int
```
## Negated
```py
x = -1
y = -1234567890987654321
z = --987
reveal_type(x) # revealed: Literal[-1]
reveal_type(y) # revealed: Literal[-1234567890987654321]
reveal_type(z) # revealed: Literal[987]
```
## Floats
```py
reveal_type(1.0) # revealed: float
```
## Complex
```py
reveal_type(2j) # revealed: complex
```

View File

@@ -0,0 +1,26 @@
# String literals
## Simple
```py
w = "Hello"
x = 'world'
y = "Guten " + 'tag'
z = 'bon ' + "jour"
reveal_type(w) # revealed: Literal["Hello"]
reveal_type(x) # revealed: Literal["world"]
reveal_type(y) # revealed: Literal["Guten tag"]
reveal_type(z) # revealed: Literal["bon jour"]
```
## Nested Quotes
```py
x = 'I say "hello" to you'
y = "You say \"hey\" back"
z = 'No "closure here'
reveal_type(x) # revealed: Literal["I say \"hello\" to you"]
reveal_type(y) # revealed: Literal["You say \"hey\" back"]
reveal_type(z) # revealed: Literal["No \"closure here"]
```

View File

@@ -0,0 +1,41 @@
# Async
Async `for` loops do not work according to the synchronous iteration protocol.
## Invalid async for loop
```py
async def foo():
class Iterator:
def __next__(self) -> int:
return 42
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
async for x in Iterator():
pass
# TODO
reveal_type(x) # revealed: Unbound | @Todo
```
## Basic async for loop
```py
async def foo():
class IntAsyncIterator:
async def __anext__(self) -> int:
return 42
class IntAsyncIterable:
def __aiter__(self) -> IntAsyncIterator:
return IntAsyncIterator()
#TODO(Alex): async iterables/iterators!
async for x in IntAsyncIterable():
pass
reveal_type(x) # revealed: Unbound | @Todo
```

View File

@@ -0,0 +1,134 @@
# For loops
## Basic `for` loop
```py
class IntIterator:
def __next__(self) -> int:
return 42
class IntIterable:
def __iter__(self) -> IntIterator:
return IntIterator()
for x in IntIterable():
pass
reveal_type(x) # revealed: Unbound | int
```
## With previous definition
```py
class IntIterator:
def __next__(self) -> int:
return 42
class IntIterable:
def __iter__(self) -> IntIterator:
return IntIterator()
x = 'foo'
for x in IntIterable():
pass
reveal_type(x) # revealed: Literal["foo"] | int
```
## With `else` (no break)
```py
class IntIterator:
def __next__(self) -> int:
return 42
class IntIterable:
def __iter__(self) -> IntIterator:
return IntIterator()
for x in IntIterable():
pass
else:
x = 'foo'
reveal_type(x) # revealed: Literal["foo"]
```
## May `break`
```py
class IntIterator:
def __next__(self) -> int:
return 42
class IntIterable:
def __iter__(self) -> IntIterator:
return IntIterator()
for x in IntIterable():
if x > 5:
break
else:
x = 'foo'
reveal_type(x) # revealed: int | Literal["foo"]
```
## With old-style iteration protocol
```py
class OldStyleIterable:
def __getitem__(self, key: int) -> int:
return 42
for x in OldStyleIterable():
pass
reveal_type(x) # revealed: Unbound | int
```
## With heterogeneous tuple
```py
for x in (1, 'a', b'foo'):
pass
reveal_type(x) # revealed: Unbound | Literal[1] | Literal["a"] | Literal[b"foo"]
```
## With non-callable iterator
```py
class NotIterable:
if flag:
__iter__ = 1
else:
__iter__ = None
for x in NotIterable(): # error: "Object of type `NotIterable` is not iterable"
pass
reveal_type(x) # revealed: Unbound | Unknown
```
## Invalid iterable
```py
nonsense = 123
for x in nonsense: # error: "Object of type `Literal[123]` is not iterable"
pass
```
## New over old style iteration protocol
```py
class NotIterable:
def __getitem__(self, key: int) -> int:
return 42
__iter__ = None
for x in NotIterable(): # error: "Object of type `NotIterable` is not iterable"
pass
```

View File

@@ -0,0 +1,18 @@
# Iterators
## Yield must be iterable
```py
class NotIterable: pass
class Iterator:
def __next__(self) -> int:
return 42
class Iterable:
def __iter__(self) -> Iterator: ...
def generator_function():
yield from Iterable()
yield from NotIterable() # error: "Object of type `NotIterable` is not iterable"
```

View File

@@ -0,0 +1,43 @@
# While loops
## Basic While Loop
```py
x = 1
while flag:
x = 2
reveal_type(x) # revealed: Literal[1, 2]
```
## While with else (no break)
```py
x = 1
while flag:
x = 2
else:
y = x
x = 3
reveal_type(x) # revealed: Literal[3]
reveal_type(y) # revealed: Literal[1, 2]
```
## While with Else (may break)
```py
x = 1
y = 0
while flag:
x = 2
if flag2:
y = 4
break
else:
y = x
x = 3
reveal_type(x) # revealed: Literal[2, 3]
reveal_type(y) # revealed: Literal[1, 2, 4]
```

View File

@@ -0,0 +1,29 @@
# Narrowing for `is` conditionals
## `is None`
```py
x = None if flag else 1
if x is None:
# TODO the following should be simplified to 'None'
reveal_type(x) # revealed: None | Literal[1] & None
reveal_type(x) # revealed: None | Literal[1]
```
## `is` for other types
```py
class A:
...
x = A()
y = x if flag else None
if y is x:
# TODO the following should be simplified to 'A'
reveal_type(y) # revealed: A | None & A
reveal_type(y) # revealed: A | None
```

View File

@@ -0,0 +1,40 @@
# Narrowing for `is not` conditionals
## `is not None`
The type guard removes `None` from the union type:
```py
x = None if flag else 1
if x is not None:
reveal_type(x) # revealed: Literal[1]
reveal_type(x) # revealed: None | Literal[1]
```
## `is not` for other singleton types
```py
x = True if flag else False
reveal_type(x) # revealed: bool
if x is not False:
# TODO the following should be `Literal[True]`
reveal_type(x) # revealed: bool & ~Literal[False]
```
## `is not` for non-singleton types
Non-singleton types should *not* narrow the type: two instances of a
non-singleton class may occupy different addresses in memory even if
they compare equal.
```py
x = [1]
y = [1]
if x is not y:
# TODO: should include type parameter: list[int]
reveal_type(x) # revealed: list
```

View File

@@ -0,0 +1,17 @@
# Narrowing for `match` statements
## Single `match` pattern
```py
x = None if flag else 1
reveal_type(x) # revealed: None | Literal[1]
y = 0
match x:
case None:
y = x
# TODO intersection simplification: should be just Literal[0] | None
reveal_type(y) # revealed: Literal[0] | None | Literal[1] & None
```

View File

@@ -0,0 +1,9 @@
# `is not None` narrowing
```py
x = None if flag else 1
if x is not None:
reveal_type(x) # revealed: Literal[1]
reveal_type(x) # revealed: None | Literal[1]
```

View File

@@ -0,0 +1,17 @@
# Classes shadowing
## Implicit error
```py
class C: pass
C = 1 # error: "Implicit shadowing of class `C`; annotate to make it explicit if this is intentional"
```
## Explicit
No diagnostic is raised in the case of explicit shadowing:
```py
class C: pass
C: int = 1
```

View File

@@ -0,0 +1,24 @@
# Function shadowing
## Parameter
Parameter `x` of type `str` is shadowed and reassigned with a new `int` value inside the function. No diagnostics should be generated.
```py path=a.py
def f(x: str):
x: int = int(x)
```
## Implicit error
```py path=a.py
def f(): pass
f = 1 # error: "Implicit shadowing of function `f`; annotate to make it explicit if this is intentional"
```
## Explicit shadowing
```py path=a.py
def f(): pass
f: int = 1
```

View File

@@ -0,0 +1,11 @@
# Shadwing declaration
## Shadow after incompatible declarations is OK
```py
if flag:
x: str
else:
x: int
x: bytes = b'foo'
```

View File

@@ -0,0 +1,10 @@
# Class defenitions in stubs
## Cyclical class definition
In type stubs, classes can reference themselves in their base class definitions. For example, in `typeshed`, we have `class str(Sequence[str]): ...`.
```py path=a.pyi
class C(C): ...
reveal_type(C) # revealed: Literal[C]
```

View File

@@ -0,0 +1,15 @@
# Bytes subscript
## Simple
```py
w = b'red' b'knot'
x = b'hello'
y = b'world' + b'!'
z = b'\xff\x00'
reveal_type(w) # revealed: Literal[b"redknot"]
reveal_type(x) # revealed: Literal[b"hello"]
reveal_type(y) # revealed: Literal[b"world!"]
reveal_type(z) # revealed: Literal[b"\xff\x00"]
```

View File

@@ -0,0 +1,92 @@
# Class subscript
## Class getitem unbound
```py
class NotSubscriptable: pass
a = NotSubscriptable[0] # error: "Cannot subscript object of type `Literal[NotSubscriptable]` with no `__class_getitem__` method"
```
## Class getitem
```py
class Identity:
def __class_getitem__(cls, item: int) -> str:
return item
a = Identity[0]
reveal_type(a) # revealed: str
```
## Class getitem union
```py
flag = True
class Identity:
if flag:
def __class_getitem__(cls, item: int) -> str:
return item
else:
def __class_getitem__(cls, item: int) -> int:
return item
a = Identity[0]
reveal_type(a) # revealed: str | int
```
## Class getitem with class union
```py
flag = True
class Identity1:
def __class_getitem__(cls, item: int) -> str:
return item
class Identity2:
def __class_getitem__(cls, item: int) -> int:
return item
if flag:
a = Identity1
else:
a = Identity2
b = a[0]
reveal_type(a) # revealed: Literal[Identity1, Identity2]
reveal_type(b) # revealed: str | int
```
## Class getitem with unbound method union
```py
flag = True
if flag:
class Identity:
def __class_getitem__(self, x: int) -> str:
pass
else:
class Identity: pass
a = Identity[42] # error: [call-non-callable] "Method `__class_getitem__` of type `Literal[__class_getitem__] | Unbound` is not callable on object of type `Literal[Identity, Identity]`"
reveal_type(a) # revealed: str | Unknown
```
## TODO: Class getitem non-class union
```py
flag = True
if flag:
class Identity:
def __class_getitem__(self, x: int) -> str:
pass
else:
Identity = 1
a = Identity[42] # error: "Cannot subscript object of type `Literal[Identity] | Literal[1]` with no `__getitem__` method"
# TODO: should _probably_ emit `str | Unknown`
reveal_type(a) # revealed: Unknown
```

View File

@@ -0,0 +1,45 @@
# Instance subscript
## Getitem unbound
```py
class NotSubscriptable: pass
a = NotSubscriptable()[0] # error: "Cannot subscript object of type `NotSubscriptable` with no `__getitem__` method"
```
## Getitem not callable
```py
class NotSubscriptable:
__getitem__ = None
a = NotSubscriptable()[0] # error: "Method `__getitem__` of type `None` is not callable on object of type `NotSubscriptable`"
```
## Valid getitem
```py
class Identity:
def __getitem__(self, index: int) -> int:
return index
a = Identity()[0]
reveal_type(a) # revealed: int
```
## Getitem union
```py
flag = True
class Identity:
if flag:
def __getitem__(self, index: int) -> int:
return index
else:
def __getitem__(self, index: int) -> str:
return str(index)
a = Identity()[0]
reveal_type(a) # revealed: int | str
```

View File

@@ -0,0 +1,32 @@
# Subscript on strings
## Simple
```py
s = 'abcde'
a = s[0]
b = s[1]
c = s[-1]
d = s[-2]
e = s[8] # error: [index-out-of-bounds] "Index 8 is out of bounds for string `Literal["abcde"]` with length 5"
f = s[-8] # error: [index-out-of-bounds] "Index -8 is out of bounds for string `Literal["abcde"]` with length 5"
reveal_type(a) # revealed: Literal["a"]
reveal_type(b) # revealed: Literal["b"]
reveal_type(c) # revealed: Literal["e"]
reveal_type(d) # revealed: Literal["d"]
reveal_type(e) # revealed: Unknown
reveal_type(f) # revealed: Unknown
```
## Function return
```py
def add(x: int, y: int) -> int:
return x + y
a = 'abcde'[add(0, 1)]
# TODO: Support overloads... Should be `str`
reveal_type(a) # revealed: @Todo
```

View File

@@ -0,0 +1,21 @@
# Tuple subscripts
## Basic
```py
t = (1, 'a', 'b')
a = t[0]
b = t[1]
c = t[-1]
d = t[-2]
e = t[4] # error: [index-out-of-bounds]
f = t[-4] # error: [index-out-of-bounds]
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal["a"]
reveal_type(c) # revealed: Literal["b"]
reveal_type(d) # revealed: Literal["a"]
reveal_type(e) # revealed: Unknown
reveal_type(f) # revealed: Unknown
```

View File

@@ -0,0 +1,37 @@
# Unary Operations
## Unary Addition
```py
a = +0
b = +1
c = +True
reveal_type(a) # revealed: Literal[0]
reveal_type(b) # revealed: Literal[1]
reveal_type(c) # revealed: Literal[1]
```
## Unary Subtraction
```py
a = -0
b = -1
c = -True
reveal_type(a) # revealed: Literal[0]
reveal_type(b) # revealed: Literal[-1]
reveal_type(c) # revealed: Literal[-1]
```
## Unary Bitwise Inversion
```py
a = ~0
b = ~1
c = ~True
reveal_type(a) # revealed: Literal[-1]
reveal_type(b) # revealed: Literal[-2]
reveal_type(c) # revealed: Literal[-2]
```

View File

@@ -0,0 +1,149 @@
# Unary not
## None
```py
a = not None
b = not not None
reveal_type(a) # revealed: Literal[True]
reveal_type(b) # revealed: Literal[False]
```
## Function
```py
from typing import reveal_type
def f():
return 1
a = not f
b = not reveal_type
reveal_type(a) # revealed: Literal[False]
# TODO Unknown should not be part of the type of typing.reveal_type
# reveal_type(b) revealed: Literal[False]
```
## Module
```py
import b; import warnings
x = not b
z = not warnings
reveal_type(x) # revealed: Literal[False]
reveal_type(z) # revealed: Literal[False]
```
```py path=b.py
y = 1
```
## Union
```py
if flag:
p = 1
q = 3.3
r = "hello"
s = "world"
t = 0
else:
p = "hello"
q = 4
r = ""
s = 0
t = ""
a = not p
b = not q
c = not r
d = not s
e = not t
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: bool
reveal_type(c) # revealed: bool
reveal_type(d) # revealed: bool
reveal_type(e) # revealed: Literal[True]
```
## Integer literal
```py
a = not 1
b = not 1234567890987654321
e = not 0
x = not -1
y = not -1234567890987654321
z = not --987
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: Literal[False]
reveal_type(e) # revealed: Literal[True]
reveal_type(x) # revealed: Literal[False]
reveal_type(y) # revealed: Literal[False]
reveal_type(z) # revealed: Literal[False]
```
## Boolean literal
```py
w = True
x = False
y = not w
z = not x
reveal_type(w) # revealed: Literal[True]
reveal_type(x) # revealed: Literal[False]
reveal_type(y) # revealed: Literal[False]
reveal_type(z) # revealed: Literal[True]
```
## String literal
```py
a = not "hello"
b = not ""
c = not "0"
d = not "hello" + "world"
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: Literal[True]
reveal_type(c) # revealed: Literal[False]
reveal_type(d) # revealed: Literal[False]
```
## Bytes literal
```py
a = not b"hello"
b = not b""
c = not b"0"
d = not b"hello" + b"world"
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: Literal[True]
reveal_type(c) # revealed: Literal[False]
reveal_type(d) # revealed: Literal[False]
```
## Tuple
```py
a = not (1,)
b = not (1, 2)
c = not (1, 2, 3)
d = not ()
e = not ("hello",)
f = not (1, "hello")
reveal_type(a) # revealed: Literal[False]
reveal_type(b) # revealed: Literal[False]
reveal_type(c) # revealed: Literal[False]
reveal_type(d) # revealed: Literal[True]
reveal_type(e) # revealed: Literal[False]
reveal_type(f) # revealed: Literal[False]
```

View File

@@ -0,0 +1,273 @@
# Unpacking
## Tuple
### Simple tuple
```py
(a, b, c) = (1, 2, 3)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
```
### Simple list
```py
[a, b, c] = (1, 2, 3)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
```
### Simple mixed
```py
[a, (b, c), d] = (1, (2, 3), 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
reveal_type(d) # revealed: Literal[4]
```
### Multiple assignment
```py
a, b = c = 1, 2
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: tuple[Literal[1], Literal[2]]
```
### Nested tuple with unpacking
```py
(a, (b, c), d) = (1, (2, 3), 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Literal[3]
reveal_type(d) # revealed: Literal[4]
```
### Nested tuple without unpacking
```py
(a, b, c) = (1, (2, 3), 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: tuple[Literal[2], Literal[3]]
reveal_type(c) # revealed: Literal[4]
```
### Uneven unpacking (1)
```py
# TODO: Add diagnostic (there aren't enough values to unpack)
(a, b, c) = (1, 2)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
reveal_type(c) # revealed: Unknown
```
### Uneven unpacking (2)
```py
# TODO: Add diagnostic (too many values to unpack)
(a, b) = (1, 2, 3)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
```
### Starred expression (1)
```py
# TODO: Add diagnostic (need more values to unpack)
# TODO: Remove 'not-iterable' diagnostic
[a, *b, c, d] = (1, 2) # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: Literal[1]
# TODO: Should be list[Any] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: Literal[2]
reveal_type(d) # revealed: Unknown
```
### Starred expression (2)
```py
[a, *b, c] = (1, 2) # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: Literal[1]
# TODO: Should be list[Any] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: Literal[2]
```
### Starred expression (3)
```py
# TODO: Remove 'not-iterable' diagnostic
[a, *b, c] = (1, 2, 3) # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: Literal[1]
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: Literal[3]
```
### Starred expression (4)
```py
# TODO: Remove 'not-iterable' diagnostic
[a, *b, c, d] = (1, 2, 3, 4, 5, 6) # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: Literal[1]
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: Literal[5]
reveal_type(d) # revealed: Literal[6]
```
### Starred expression (5)
```py
# TODO: Remove 'not-iterable' diagnostic
[a, b, *c] = (1, 2, 3, 4) # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(c) # revealed: @Todo
```
### Non-iterable unpacking
TODO: Remove duplicate diagnostics. This is happening because for a sequence-like
assignment target, multiple definitions are created and the inference engine runs
on each of them which results in duplicate diagnostics.
```py
# error: "Object of type `Literal[1]` is not iterable"
# error: "Object of type `Literal[1]` is not iterable"
a, b = 1
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
```
### Custom iterator unpacking
```py
class Iterator:
def __next__(self) -> int:
return 42
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
(a, b) = Iterable()
reveal_type(a) # revealed: int
reveal_type(b) # revealed: int
```
### Custom iterator unpacking nested
```py
class Iterator:
def __next__(self) -> int:
return 42
class Iterable:
def __iter__(self) -> Iterator:
return Iterator()
(a, (b, c), d) = (1, Iterable(), 2)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: int
reveal_type(c) # revealed: int
reveal_type(d) # revealed: Literal[2]
```
## String
### Simple unpacking
```py
a, b = 'ab'
reveal_type(a) # revealed: LiteralString
reveal_type(b) # revealed: LiteralString
```
### Uneven unpacking (1)
```py
# TODO: Add diagnostic (there aren't enough values to unpack)
a, b, c = 'ab'
reveal_type(a) # revealed: LiteralString
reveal_type(b) # revealed: LiteralString
reveal_type(c) # revealed: Unknown
```
### Uneven unpacking (2)
```py
# TODO: Add diagnostic (too many values to unpack)
a, b = 'abc'
reveal_type(a) # revealed: LiteralString
reveal_type(b) # revealed: LiteralString
```
### Starred expression (1)
```py
# TODO: Add diagnostic (need more values to unpack)
# TODO: Remove 'not-iterable' diagnostic
(a, *b, c, d) = "ab" # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: LiteralString
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: LiteralString
reveal_type(d) # revealed: Unknown
```
### Starred expression (2)
```py
(a, *b, c) = "ab" # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: LiteralString
# TODO: Should be list[Any] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: LiteralString
```
### Starred expression (3)
```py
# TODO: Remove 'not-iterable' diagnostic
(a, *b, c) = "abc" # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: LiteralString
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: LiteralString
```
### Starred expression (4)
```py
# TODO: Remove 'not-iterable' diagnostic
(a, *b, c, d) = "abcdef" # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: LiteralString
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo
reveal_type(c) # revealed: LiteralString
reveal_type(d) # revealed: LiteralString
```
### Starred expression (5)
```py
# TODO: Remove 'not-iterable' diagnostic
(a, b, *c) = "abcd" # error: "Object of type `None` is not iterable"
reveal_type(a) # revealed: LiteralString
reveal_type(b) # revealed: LiteralString
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(c) # revealed: @Todo
```

View File

@@ -11,7 +11,6 @@ pub trait Db: SourceDb + Upcast<dyn SourceDb> {
pub(crate) mod tests {
use std::sync::Arc;
use crate::module_resolver::vendored_typeshed_stubs;
use ruff_db::files::{File, Files};
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
use ruff_db::vendored::VendoredFileSystem;
@@ -33,7 +32,7 @@ pub(crate) mod tests {
Self {
storage: salsa::Storage::default(),
system: TestSystem::default(),
vendored: vendored_typeshed_stubs().clone(),
vendored: red_knot_vendored::file_system().clone(),
events: std::sync::Arc::default(),
files: Files::default(),
}

View File

@@ -4,9 +4,7 @@ use rustc_hash::FxHasher;
pub use db::Db;
pub use module_name::ModuleName;
pub use module_resolver::{
resolve_module, system_module_search_paths, vendored_typeshed_stubs, Module,
};
pub use module_resolver::{resolve_module, system_module_search_paths, Module};
pub use program::{Program, ProgramSettings, SearchPathSettings, SitePackages};
pub use python_version::PythonVersion;
pub use semantic_model::{HasTy, SemanticModel};

View File

@@ -4,7 +4,6 @@ pub use module::Module;
pub use resolver::resolve_module;
pub(crate) use resolver::{file_to_module, SearchPaths};
use ruff_db::system::SystemPath;
pub use typeshed::vendored_typeshed_stubs;
use crate::module_resolver::resolver::search_paths;
use crate::Db;

View File

@@ -1,22 +1,23 @@
use rustc_hash::{FxBuildHasher, FxHashSet};
use std::borrow::Cow;
use std::iter::FusedIterator;
use std::ops::Deref;
use rustc_hash::{FxBuildHasher, FxHashSet};
use ruff_db::files::{File, FilePath, FileRootKind};
use ruff_db::system::{DirectoryEntry, System, SystemPath, SystemPathBuf};
use ruff_db::vendored::{VendoredFileSystem, VendoredPath};
use super::module::{Module, ModuleKind};
use super::path::{ModulePath, SearchPath, SearchPathValidationError};
use crate::db::Db;
use crate::module_name::ModuleName;
use crate::module_resolver::typeshed::{vendored_typeshed_versions, TypeshedVersions};
use crate::site_packages::VirtualEnvironment;
use crate::{Program, PythonVersion, SearchPathSettings, SitePackages};
use super::module::{Module, ModuleKind};
use super::path::{ModulePath, SearchPath, SearchPathValidationError};
/// Resolves a module name to a module.
pub fn resolve_module(db: &dyn Db, module_name: ModuleName) -> Option<Module> {
pub fn resolve_module(db: &dyn Db, module_name: &ModuleName) -> Option<Module> {
let interned_name = ModuleNameIngredient::new(db, module_name);
resolve_module_query(db, interned_name)
@@ -35,14 +36,14 @@ pub(crate) fn resolve_module_query<'db>(
let _span = tracing::trace_span!("resolve_module", %name).entered();
let Some((search_path, module_file, kind)) = resolve_name(db, name) else {
tracing::debug!("Module '{name}' not found in the search paths.");
tracing::debug!("Module `{name}` not found in search paths");
return None;
};
let module = Module::new(name.clone(), kind, search_path, module_file);
tracing::trace!(
"Resolved module '{name}' to '{path}'.",
"Resolved module `{name}` to `{path}`",
path = module_file.path(db)
);
@@ -102,7 +103,7 @@ pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
// If it doesn't, then that means that multiple modules have the same name in different
// root paths, but that the module corresponding to `path` is in a lower priority search path,
// in which case we ignore it.
let module = resolve_module(db, module_name)?;
let module = resolve_module(db, &module_name)?;
if file == module.file() {
Some(module)
@@ -136,7 +137,7 @@ pub(crate) struct SearchPaths {
/// for the first `site-packages` path
site_packages: Vec<SearchPath>,
typeshed_versions: ResolvedTypeshedVersions,
typeshed_versions: TypeshedVersions,
}
impl SearchPaths {
@@ -202,11 +203,11 @@ impl SearchPaths {
let search_path = SearchPath::custom_stdlib(db, &custom_typeshed)?;
(ResolvedTypeshedVersions::Custom(parsed), search_path)
(parsed, search_path)
} else {
tracing::debug!("Using vendored stdlib");
(
ResolvedTypeshedVersions::Vendored(vendored_typeshed_versions()),
vendored_typeshed_versions(db),
SearchPath::vendored_stdlib(),
)
};
@@ -279,23 +280,6 @@ impl SearchPaths {
}
}
#[derive(Debug, PartialEq, Eq)]
enum ResolvedTypeshedVersions {
Vendored(&'static TypeshedVersions),
Custom(TypeshedVersions),
}
impl Deref for ResolvedTypeshedVersions {
type Target = TypeshedVersions;
fn deref(&self) -> &Self::Target {
match self {
ResolvedTypeshedVersions::Vendored(versions) => versions,
ResolvedTypeshedVersions::Custom(versions) => versions,
}
}
}
/// Collect all dynamic search paths. For each `site-packages` path:
/// - Collect that `site-packages` path
/// - Collect any search paths listed in `.pth` files in that `site-packages` directory
@@ -340,7 +324,7 @@ pub(crate) fn dynamic_resolution_paths(db: &dyn Db) -> Vec<SearchPath> {
let site_packages_root = files
.root(db.upcast(), site_packages_dir)
.expect("Site-package root to have been created.");
.expect("Site-package root to have been created");
// This query needs to be re-executed each time a `.pth` file
// is added, modified or removed from the `site-packages` directory.
@@ -744,11 +728,11 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(
Some(&foo_module),
resolve_module(&db, foo_module_name.clone()).as_ref()
resolve_module(&db, &foo_module_name).as_ref()
);
assert_eq!("foo", foo_module.name());
@@ -771,7 +755,7 @@ mod tests {
.build();
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, builtins_module_name).expect("builtins to resolve");
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi"));
}
@@ -792,7 +776,7 @@ mod tests {
.build();
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, builtins_module_name).expect("builtins to resolve");
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi"));
}
@@ -810,11 +794,11 @@ mod tests {
.build();
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(
Some(&functools_module),
resolve_module(&db, functools_module_name).as_ref()
resolve_module(&db, &functools_module_name).as_ref()
);
assert_eq!(&stdlib, functools_module.search_path());
@@ -864,7 +848,7 @@ mod tests {
let existing_modules = create_module_names(&["asyncio", "functools", "xml.etree"]);
for module_name in existing_modules {
let resolved_module = resolve_module(&db, module_name.clone()).unwrap_or_else(|| {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib")
});
let search_path = resolved_module.search_path();
@@ -917,7 +901,7 @@ mod tests {
for module_name in nonexisting_modules {
assert!(
resolve_module(&db, module_name.clone()).is_none(),
resolve_module(&db, &module_name).is_none(),
"Unexpectedly resolved a module for {module_name}"
);
}
@@ -960,7 +944,7 @@ mod tests {
]);
for module_name in existing_modules {
let resolved_module = resolve_module(&db, module_name.clone()).unwrap_or_else(|| {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib")
});
let search_path = resolved_module.search_path();
@@ -996,7 +980,7 @@ mod tests {
let nonexisting_modules = create_module_names(&["importlib", "xml", "xml.etree"]);
for module_name in nonexisting_modules {
assert!(
resolve_module(&db, module_name.clone()).is_none(),
resolve_module(&db, &module_name).is_none(),
"Unexpectedly resolved a module for {module_name}"
);
}
@@ -1018,11 +1002,11 @@ mod tests {
.build();
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(
Some(&functools_module),
resolve_module(&db, functools_module_name).as_ref()
resolve_module(&db, &functools_module_name).as_ref()
);
assert_eq!(&src, functools_module.search_path());
assert_eq!(ModuleKind::Module, functools_module.kind());
@@ -1042,7 +1026,7 @@ mod tests {
.build();
let pydoc_data_topics_name = ModuleName::new_static("pydoc_data.topics").unwrap();
let pydoc_data_topics = resolve_module(&db, pydoc_data_topics_name).unwrap();
let pydoc_data_topics = resolve_module(&db, &pydoc_data_topics_name).unwrap();
assert_eq!("pydoc_data.topics", pydoc_data_topics.name());
assert_eq!(pydoc_data_topics.search_path(), &stdlib);
@@ -1059,7 +1043,7 @@ mod tests {
.build();
let foo_path = src.join("foo/__init__.py");
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!("foo", foo_module.name());
assert_eq!(&src, foo_module.search_path());
@@ -1086,7 +1070,7 @@ mod tests {
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&src, foo_module.search_path());
@@ -1114,11 +1098,11 @@ mod tests {
let foo_bar_module_name = ModuleName::new_static("foo.bar").unwrap();
// `foo.py` takes priority over the `foo` namespace package
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(foo_module.file().path(&db), &src.join("foo.py"));
// `foo.bar` isn't recognised as a module
let foo_bar_module = resolve_module(&db, foo_bar_module_name.clone());
let foo_bar_module = resolve_module(&db, &foo_bar_module_name);
assert_eq!(foo_bar_module, None);
}
@@ -1128,7 +1112,7 @@ mod tests {
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let foo = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
let foo = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_stub = src.join("foo.pyi");
assert_eq!(&src, foo.search_path());
@@ -1152,7 +1136,7 @@ mod tests {
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let baz_module =
resolve_module(&db, ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
resolve_module(&db, &ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
let baz_path = src.join("foo/bar/baz.py");
assert_eq!(&src, baz_module.search_path());
@@ -1191,14 +1175,14 @@ mod tests {
let one_module_name = ModuleName::new_static("parent.child.one").unwrap();
let one_module_path = FilePath::System(src.join("parent/child/one.py"));
assert_eq!(
resolve_module(&db, one_module_name),
resolve_module(&db, &one_module_name),
path_to_module(&db, &one_module_path)
);
let two_module_name = ModuleName::new_static("parent.child.two").unwrap();
let two_module_path = FilePath::System(site_packages.join("parent/child/two.py"));
assert_eq!(
resolve_module(&db, two_module_name),
resolve_module(&db, &two_module_name),
path_to_module(&db, &two_module_path)
);
}
@@ -1231,12 +1215,12 @@ mod tests {
let one_module_path = FilePath::System(src.join("parent/child/one.py"));
let one_module_name =
resolve_module(&db, ModuleName::new_static("parent.child.one").unwrap());
resolve_module(&db, &ModuleName::new_static("parent.child.one").unwrap());
assert_eq!(one_module_name, path_to_module(&db, &one_module_path));
assert_eq!(
None,
resolve_module(&db, ModuleName::new_static("parent.child.two").unwrap())
resolve_module(&db, &ModuleName::new_static("parent.child.two").unwrap())
);
}
@@ -1252,7 +1236,7 @@ mod tests {
.with_site_packages_files(&[("foo.py", "")])
.build();
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_src_path = src.join("foo.py");
assert_eq!(&src, foo_module.search_path());
@@ -1317,8 +1301,8 @@ mod tests {
)
.context("Invalid program settings")?;
let foo_module = resolve_module(&db, ModuleName::new_static("foo").unwrap()).unwrap();
let bar_module = resolve_module(&db, ModuleName::new_static("bar").unwrap()).unwrap();
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let bar_module = resolve_module(&db, &ModuleName::new_static("bar").unwrap()).unwrap();
assert_ne!(foo_module, bar_module);
@@ -1353,7 +1337,7 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let bar_path = src.join("bar.py");
let bar = system_path_to_file(&db, &bar_path).expect("bar.py to exist");
@@ -1367,7 +1351,7 @@ mod tests {
// Re-query the foo module. The foo module should still be cached because `bar.py` isn't relevant
// for resolving `foo`.
let foo_module2 = resolve_module(&db, foo_module_name);
let foo_module2 = resolve_module(&db, &foo_module_name);
assert!(!db
.take_salsa_events()
@@ -1384,14 +1368,14 @@ mod tests {
let foo_path = src.join("foo.py");
let foo_module_name = ModuleName::new_static("foo").unwrap();
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
assert_eq!(resolve_module(&db, &foo_module_name), None);
// Now write the foo file
db.write_file(&foo_path, "x = 1")?;
let foo_file = system_path_to_file(&db, &foo_path).expect("foo.py to exist");
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(foo_file, foo_module.file());
Ok(())
@@ -1405,7 +1389,7 @@ mod tests {
let TestCase { mut db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).expect("foo module to exist");
let foo_module = resolve_module(&db, &foo_module_name).expect("foo module to exist");
let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&foo_init_path, foo_module.file().path(&db));
@@ -1417,7 +1401,7 @@ mod tests {
File::sync_path(&mut db, &foo_init_path);
File::sync_path(&mut db, foo_init_path.parent().unwrap());
let foo_module = resolve_module(&db, foo_module_name).expect("Foo module to resolve");
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(&src.join("foo.py"), foo_module.file().path(&db));
Ok(())
@@ -1443,7 +1427,7 @@ mod tests {
let functools_module_name = ModuleName::new_static("functools").unwrap();
let stdlib_functools_path = stdlib.join("functools.pyi");
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(
Ok(functools_module.file()),
@@ -1456,7 +1440,7 @@ mod tests {
let site_packages_functools_path = site_packages.join("functools.py");
db.write_file(&site_packages_functools_path, "f: int")
.unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let events = db.take_salsa_events();
assert_function_query_was_not_run(
&db,
@@ -1489,7 +1473,7 @@ mod tests {
.build();
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(
Ok(functools_module.file()),
@@ -1500,7 +1484,7 @@ mod tests {
// since first-party files take higher priority in module resolution:
let src_functools_path = src.join("functools.py");
db.write_file(&src_functools_path, "FOO: int").unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &src);
assert_eq!(
Ok(functools_module.file()),
@@ -1531,7 +1515,7 @@ mod tests {
let functools_module_name = ModuleName::new_static("functools").unwrap();
let src_functools_path = src.join("functools.py");
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &src);
assert_eq!(
Ok(functools_module.file()),
@@ -1544,7 +1528,7 @@ mod tests {
.remove_file(&src_functools_path)
.unwrap();
File::sync_path(&mut db, &src_functools_path);
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(
Ok(functools_module.file()),
@@ -1566,8 +1550,8 @@ mod tests {
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_bar_module_name = ModuleName::new_static("foo.bar").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let foo_bar_module = resolve_module(&db, foo_bar_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_bar_module = resolve_module(&db, &foo_bar_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
@@ -1595,11 +1579,11 @@ mod tests {
// Lines with leading whitespace in `.pth` files do not parse:
let foo_module_name = ModuleName::new_static("foo").unwrap();
assert_eq!(resolve_module(&db, foo_module_name), None);
assert_eq!(resolve_module(&db, &foo_module_name), None);
// Lines with trailing whitespace in `.pth` files do:
let bar_module_name = ModuleName::new_static("bar").unwrap();
let bar_module = resolve_module(&db, bar_module_name.clone()).unwrap();
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
assert_eq!(
bar_module.file().path(&db),
&FilePath::system("/y/src/bar.py")
@@ -1618,7 +1602,7 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
@@ -1666,10 +1650,10 @@ not_a_directory
let b_module_name = ModuleName::new_static("b").unwrap();
let spam_module_name = ModuleName::new_static("spam").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let a_module = resolve_module(&db, a_module_name.clone()).unwrap();
let b_module = resolve_module(&db, b_module_name.clone()).unwrap();
let spam_module = resolve_module(&db, spam_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let a_module = resolve_module(&db, &a_module_name).unwrap();
let b_module = resolve_module(&db, &b_module_name).unwrap();
let spam_module = resolve_module(&db, &spam_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
@@ -1697,14 +1681,14 @@ not_a_directory
let foo_module_name = ModuleName::new_static("foo").unwrap();
let bar_module_name = ModuleName::new_static("bar").unwrap();
let foo_module = resolve_module(&db, foo_module_name).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
&FilePath::system("/x/src/foo.py")
);
db.clear_salsa_events();
let bar_module = resolve_module(&db, bar_module_name).unwrap();
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
assert_eq!(
bar_module.file().path(&db),
&FilePath::system("/y/src/bar.py")
@@ -1729,7 +1713,7 @@ not_a_directory
db.write_files(x_directory).unwrap();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file().path(&db),
&FilePath::system("/x/src/foo.py")
@@ -1741,7 +1725,7 @@ not_a_directory
File::sync_path(&mut db, &site_packages.join("_foo.pth"));
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
assert_eq!(resolve_module(&db, &foo_module_name), None);
}
#[test]
@@ -1756,7 +1740,7 @@ not_a_directory
db.write_files(x_directory).unwrap();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, foo_module_name.clone()).unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let src_path = SystemPathBuf::from("/x/src");
assert_eq!(
foo_module.file().path(&db),
@@ -1769,7 +1753,7 @@ not_a_directory
db.memory_file_system().remove_directory(&src_path).unwrap();
File::sync_path(&mut db, &src_path.join("foo.py"));
File::sync_path(&mut db, &src_path);
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
assert_eq!(resolve_module(&db, &foo_module_name), None);
}
#[test]
@@ -1828,7 +1812,7 @@ not_a_directory
// The editable installs discovered from the `.pth` file in the first `site-packages` directory
// take precedence over the second `site-packages` directory...
let a_module_name = ModuleName::new_static("a").unwrap();
let a_module = resolve_module(&db, a_module_name.clone()).unwrap();
let a_module = resolve_module(&db, &a_module_name).unwrap();
assert_eq!(a_module.file().path(&db), &editable_install_location);
db.memory_file_system()
@@ -1839,7 +1823,7 @@ not_a_directory
// ...But now that the `.pth` file in the first `site-packages` directory has been deleted,
// the editable install no longer exists, so the module now resolves to the file in the
// second `site-packages` directory
let a_module = resolve_module(&db, a_module_name).unwrap();
let a_module = resolve_module(&db, &a_module_name).unwrap();
assert_eq!(a_module.file().path(&db), &system_site_packages_location);
}
}

View File

@@ -4,25 +4,19 @@ use std::num::{NonZeroU16, NonZeroUsize};
use std::ops::{RangeFrom, RangeInclusive};
use std::str::FromStr;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use super::vendored::vendored_typeshed_stubs;
use crate::db::Db;
use crate::module_name::ModuleName;
use crate::{Program, PythonVersion};
static VENDORED_VERSIONS: Lazy<TypeshedVersions> = Lazy::new(|| {
pub(in crate::module_resolver) fn vendored_typeshed_versions(db: &dyn Db) -> TypeshedVersions {
TypeshedVersions::from_str(
&vendored_typeshed_stubs()
&db.vendored()
.read_to_string("stdlib/VERSIONS")
.unwrap(),
.expect("The vendored typeshed stubs should contain a VERSIONS file"),
)
.unwrap()
});
pub(crate) fn vendored_typeshed_versions() -> &'static TypeshedVersions {
&VENDORED_VERSIONS
.expect("The VERSIONS file in the vendored typeshed stubs should be well-formed")
}
pub(crate) fn typeshed_versions(db: &dyn Db) -> &TypeshedVersions {
@@ -332,6 +326,8 @@ mod tests {
use insta::assert_snapshot;
use crate::db::tests::TestDb;
use super::*;
const TYPESHED_STDLIB_DIR: &str = "stdlib";
@@ -353,12 +349,9 @@ mod tests {
#[test]
fn can_parse_vendored_versions_file() {
let versions_data = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/vendor/typeshed/stdlib/VERSIONS"
));
let db = TestDb::new();
let versions = TypeshedVersions::from_str(versions_data).unwrap();
let versions = vendored_typeshed_versions(&db);
assert!(versions.len() > 100);
assert!(versions.len() < 1000);
@@ -395,9 +388,10 @@ mod tests {
#[test]
fn typeshed_versions_consistent_with_vendored_stubs() {
const VERSIONS_DATA: &str = include_str!("../../../vendor/typeshed/stdlib/VERSIONS");
let vendored_typeshed_dir = Path::new("vendor/typeshed").canonicalize().unwrap();
let vendored_typeshed_versions = TypeshedVersions::from_str(VERSIONS_DATA).unwrap();
let db = TestDb::new();
let vendored_typeshed_versions = vendored_typeshed_versions(&db);
let vendored_typeshed_dir =
Path::new(env!("CARGO_MANIFEST_DIR")).join("../red_knot_vendored/vendor/typeshed");
let mut empty_iterator = true;

View File

@@ -1,8 +0,0 @@
pub use self::vendored::vendored_typeshed_stubs;
pub(super) use self::versions::{
typeshed_versions, vendored_typeshed_versions, TypeshedVersions, TypeshedVersionsParseError,
TypeshedVersionsQueryResult,
};
mod vendored;
mod versions;

View File

@@ -1,7 +1,7 @@
use std::iter::FusedIterator;
use std::sync::Arc;
use rustc_hash::FxHashMap;
use rustc_hash::{FxBuildHasher, FxHashMap};
use salsa::plumbing::AsId;
use ruff_db::files::File;
@@ -31,7 +31,7 @@ pub(crate) use self::use_def::{
BindingWithConstraints, BindingWithConstraintsIterator, DeclarationsIterator,
};
type SymbolMap = hashbrown::HashMap<ScopedSymbolId, (), ()>;
type SymbolMap = hashbrown::HashMap<ScopedSymbolId, (), FxBuildHasher>;
/// Returns the semantic index for `file`.
///
@@ -994,7 +994,7 @@ class C[T]:
let ast::Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(num),
..
}) = &*assignment.assignment().value
}) = assignment.value()
else {
panic!("should be a number literal")
};

View File

@@ -28,8 +28,8 @@ use crate::Db;
use super::constraint::{Constraint, PatternConstraint};
use super::definition::{
DefinitionCategory, ExceptHandlerDefinitionNodeRef, MatchPatternDefinitionNodeRef,
WithItemDefinitionNodeRef,
AssignmentKind, DefinitionCategory, ExceptHandlerDefinitionNodeRef,
MatchPatternDefinitionNodeRef, WithItemDefinitionNodeRef,
};
pub(super) struct SemanticIndexBuilder<'db> {
@@ -566,11 +566,22 @@ where
debug_assert!(self.current_assignment.is_none());
self.visit_expr(&node.value);
self.add_standalone_expression(&node.value);
self.current_assignment = Some(node.into());
for target in &node.targets {
for (target_index, target) in node.targets.iter().enumerate() {
let kind = match target {
ast::Expr::List(_) | ast::Expr::Tuple(_) => Some(AssignmentKind::Sequence),
ast::Expr::Name(_) => Some(AssignmentKind::Name),
_ => None,
};
if let Some(kind) = kind {
self.current_assignment = Some(CurrentAssignment::Assign {
assignment: node,
target_index,
kind,
});
}
self.visit_expr(target);
self.current_assignment = None;
}
self.current_assignment = None;
}
ast::Stmt::AnnAssign(node) => {
debug_assert!(self.current_assignment.is_none());
@@ -815,12 +826,18 @@ where
let symbol = self.add_symbol(id.clone());
if is_definition {
match self.current_assignment {
Some(CurrentAssignment::Assign(assignment)) => {
Some(CurrentAssignment::Assign {
assignment,
target_index,
kind,
}) => {
self.add_definition(
symbol,
AssignmentDefinitionNodeRef {
assignment,
target: name_node,
target_index,
name: name_node,
kind,
},
);
}
@@ -1045,7 +1062,11 @@ where
#[derive(Copy, Clone, Debug)]
enum CurrentAssignment<'a> {
Assign(&'a ast::StmtAssign),
Assign {
assignment: &'a ast::StmtAssign,
target_index: usize,
kind: AssignmentKind,
},
AnnAssign(&'a ast::StmtAnnAssign),
AugAssign(&'a ast::StmtAugAssign),
For(&'a ast::StmtFor),
@@ -1057,12 +1078,6 @@ enum CurrentAssignment<'a> {
WithItem(&'a ast::WithItem),
}
impl<'a> From<&'a ast::StmtAssign> for CurrentAssignment<'a> {
fn from(value: &'a ast::StmtAssign) -> Self {
Self::Assign(value)
}
}
impl<'a> From<&'a ast::StmtAnnAssign> for CurrentAssignment<'a> {
fn from(value: &'a ast::StmtAnnAssign) -> Self {
Self::AnnAssign(value)

View File

@@ -3,6 +3,7 @@ use ruff_db::parsed::ParsedModule;
use ruff_python_ast as ast;
use crate::ast_node_ref::AstNodeRef;
use crate::module_resolver::file_to_module;
use crate::node_key::NodeKey;
use crate::semantic_index::symbol::{FileScopeId, ScopeId, ScopedSymbolId};
use crate::Db;
@@ -45,6 +46,14 @@ impl<'db> Definition<'db> {
pub(crate) fn is_binding(self, db: &'db dyn Db) -> bool {
self.kind(db).category().is_binding()
}
/// Return true if this is a symbol was defined in the `typing` or `typing_extensions` modules
pub(crate) fn is_typing_definition(self, db: &'db dyn Db) -> bool {
file_to_module(db, self.file(db)).is_some_and(|module| {
module.search_path().is_standard_library()
&& matches!(&**module.name(), "typing" | "typing_extensions")
})
}
}
#[derive(Copy, Clone, Debug)]
@@ -152,7 +161,9 @@ pub(crate) struct ImportFromDefinitionNodeRef<'a> {
#[derive(Copy, Clone, Debug)]
pub(crate) struct AssignmentDefinitionNodeRef<'a> {
pub(crate) assignment: &'a ast::StmtAssign,
pub(crate) target: &'a ast::ExprName,
pub(crate) target_index: usize,
pub(crate) name: &'a ast::ExprName,
pub(crate) kind: AssignmentKind,
}
#[derive(Copy, Clone, Debug)]
@@ -215,12 +226,17 @@ impl DefinitionNodeRef<'_> {
DefinitionNodeRef::NamedExpression(named) => {
DefinitionKind::NamedExpression(AstNodeRef::new(parsed, named))
}
DefinitionNodeRef::Assignment(AssignmentDefinitionNodeRef { assignment, target }) => {
DefinitionKind::Assignment(AssignmentDefinitionKind {
assignment: AstNodeRef::new(parsed.clone(), assignment),
target: AstNodeRef::new(parsed, target),
})
}
DefinitionNodeRef::Assignment(AssignmentDefinitionNodeRef {
assignment,
target_index,
name,
kind,
}) => DefinitionKind::Assignment(AssignmentDefinitionKind {
assignment: AstNodeRef::new(parsed.clone(), assignment),
target_index,
name: AstNodeRef::new(parsed, name),
kind,
}),
DefinitionNodeRef::AnnotatedAssignment(assign) => {
DefinitionKind::AnnotatedAssignment(AstNodeRef::new(parsed, assign))
}
@@ -291,8 +307,10 @@ impl DefinitionNodeRef<'_> {
Self::NamedExpression(node) => node.into(),
Self::Assignment(AssignmentDefinitionNodeRef {
assignment: _,
target,
}) => target.into(),
target_index: _,
name,
kind: _,
}) => name.into(),
Self::AnnotatedAssignment(node) => node.into(),
Self::AugmentedAssignment(node) => node.into(),
Self::For(ForStmtDefinitionNodeRef {
@@ -476,17 +494,34 @@ impl ImportFromDefinitionKind {
#[derive(Clone, Debug)]
pub struct AssignmentDefinitionKind {
assignment: AstNodeRef<ast::StmtAssign>,
target: AstNodeRef<ast::ExprName>,
target_index: usize,
name: AstNodeRef<ast::ExprName>,
kind: AssignmentKind,
}
impl AssignmentDefinitionKind {
pub(crate) fn assignment(&self) -> &ast::StmtAssign {
self.assignment.node()
pub(crate) fn value(&self) -> &ast::Expr {
&self.assignment.node().value
}
pub(crate) fn target(&self) -> &ast::ExprName {
self.target.node()
pub(crate) fn target(&self) -> &ast::Expr {
&self.assignment.node().targets[self.target_index]
}
pub(crate) fn name(&self) -> &ast::ExprName {
self.name.node()
}
pub(crate) fn kind(&self) -> AssignmentKind {
self.kind
}
}
/// The kind of assignment target expression.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AssignmentKind {
Sequence,
Name,
}
#[derive(Clone, Debug)]

View File

@@ -35,7 +35,7 @@ impl<'db> SemanticModel<'db> {
line_index(self.db.upcast(), self.file)
}
pub fn resolve_module(&self, module_name: ModuleName) -> Option<Module> {
pub fn resolve_module(&self, module_name: &ModuleName) -> Option<Module> {
resolve_module(self.db, module_name)
}

View File

@@ -192,7 +192,7 @@ impl VirtualEnvironment {
} else {
tracing::warn!(
"Failed to resolve `sys.prefix` of the system Python installation \
from the `home` value in the `pyvenv.cfg` file at '{}'. \
from the `home` value in the `pyvenv.cfg` file at `{}`. \
System site-packages will not be used for module resolution.",
venv_path.join("pyvenv.cfg")
);
@@ -426,7 +426,7 @@ impl Deref for SysPrefixPath {
impl fmt::Display for SysPrefixPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "`sys.prefix` path '{}'", self.0)
write!(f, "`sys.prefix` path `{}`", self.0)
}
}
@@ -483,7 +483,7 @@ impl Deref for PythonHomePath {
impl fmt::Display for PythonHomePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "`home` location '{}'", self.0)
write!(f, "`home` location `{}`", self.0)
}
}

View File

@@ -35,7 +35,7 @@ fn core_module_symbol_ty<'db>(
core_module: CoreStdlibModule,
symbol: &str,
) -> Type<'db> {
resolve_module(db, core_module.name())
resolve_module(db, &core_module.name())
.map(|module| global_symbol_ty(db, module.file(), symbol))
.unwrap_or(Type::Unbound)
}
@@ -76,7 +76,7 @@ pub(crate) fn typing_extensions_symbol_ty<'db>(db: &'db dyn Db, symbol: &str) ->
///
/// Can return `None` if a custom typeshed is used that is missing the core module in question.
fn core_module_scope(db: &dyn Db, core_module: CoreStdlibModule) -> Option<ScopeId<'_>> {
resolve_module(db, core_module.name()).map(|module| global_scope(db, module.file()))
resolve_module(db, &core_module.name()).map(|module| global_scope(db, module.file()))
}
/// Get the `builtins` module scope.

File diff suppressed because it is too large Load Diff

View File

@@ -25,10 +25,12 @@
//! * No type in an intersection can be a supertype of any other type in the intersection (just
//! eliminate the supertype from the intersection).
//! * An intersection containing two non-overlapping types should simplify to [`Type::Never`].
use crate::types::{builtins_symbol_ty, IntersectionType, Type, UnionType};
use crate::types::{IntersectionType, Type, UnionType};
use crate::{Db, FxOrderSet};
use smallvec::SmallVec;
use super::KnownClass;
pub(crate) struct UnionBuilder<'db> {
elements: Vec<Type<'db>>,
db: &'db dyn Db,
@@ -64,7 +66,7 @@ impl<'db> UnionBuilder<'db> {
let mut to_remove = SmallVec::<[usize; 2]>::new();
for (index, element) in self.elements.iter().enumerate() {
if Some(*element) == bool_pair {
to_add = builtins_symbol_ty(self.db, "bool");
to_add = KnownClass::Bool.to_instance(self.db);
to_remove.push(index);
// The type we are adding is a BooleanLiteral, which doesn't have any
// subtypes. And we just found that the union already contained our
@@ -109,7 +111,7 @@ impl<'db> UnionBuilder<'db> {
match self.elements.len() {
0 => Type::Never,
1 => self.elements[0],
_ => Type::Union(UnionType::new(self.db, self.elements.into())),
_ => Type::Union(UnionType::new(self.db, self.elements.into_boxed_slice())),
}
}
}
@@ -215,6 +217,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
/// Adds a positive type to this intersection.
fn add_positive(&mut self, db: &'db dyn Db, ty: Type<'db>) {
// TODO `Any`/`Unknown`/`Todo` actually should not self-cancel
match ty {
Type::Intersection(inter) => {
let pos = inter.positive(db);
@@ -234,7 +237,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
/// Adds a negative type to this intersection.
fn add_negative(&mut self, db: &'db dyn Db, ty: Type<'db>) {
// TODO Any/Unknown actually should not self-cancel
// TODO `Any`/`Unknown`/`Todo` actually should not self-cancel
match ty {
Type::Intersection(intersection) => {
let pos = intersection.negative(db);
@@ -299,7 +302,7 @@ mod tests {
use crate::db::tests::TestDb;
use crate::program::{Program, SearchPathSettings};
use crate::python_version::PythonVersion;
use crate::types::{builtins_symbol_ty, UnionBuilder};
use crate::types::{KnownClass, UnionBuilder};
use crate::ProgramSettings;
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
@@ -359,7 +362,7 @@ mod tests {
#[test]
fn build_union_bool() {
let db = setup_db();
let bool_ty = builtins_symbol_ty(&db, "bool");
let bool_instance_ty = KnownClass::Bool.to_instance(&db);
let t0 = Type::BooleanLiteral(true);
let t1 = Type::BooleanLiteral(true);
@@ -370,7 +373,7 @@ mod tests {
assert_eq!(union.elements(&db), &[t0, t3]);
let union = UnionType::from_elements(&db, [t0, t1, t2, t3]).expect_union();
assert_eq!(union.elements(&db), &[bool_ty, t3]);
assert_eq!(union.elements(&db), &[bool_instance_ty, t3]);
}
#[test]
@@ -388,7 +391,7 @@ mod tests {
#[test]
fn build_union_simplify_subtype() {
let db = setup_db();
let t0 = builtins_symbol_ty(&db, "str").to_instance(&db);
let t0 = KnownClass::Str.to_instance(&db);
let t1 = Type::LiteralString;
let u0 = UnionType::from_elements(&db, [t0, t1]);
let u1 = UnionType::from_elements(&db, [t1, t0]);
@@ -400,7 +403,7 @@ mod tests {
#[test]
fn build_union_no_simplify_unknown() {
let db = setup_db();
let t0 = builtins_symbol_ty(&db, "str").to_instance(&db);
let t0 = KnownClass::Str.to_instance(&db);
let t1 = Type::Unknown;
let u0 = UnionType::from_elements(&db, [t0, t1]);
let u1 = UnionType::from_elements(&db, [t1, t0]);
@@ -412,9 +415,9 @@ mod tests {
#[test]
fn build_union_subsume_multiple() {
let db = setup_db();
let str_ty = builtins_symbol_ty(&db, "str").to_instance(&db);
let int_ty = builtins_symbol_ty(&db, "int").to_instance(&db);
let object_ty = builtins_symbol_ty(&db, "object").to_instance(&db);
let str_ty = KnownClass::Str.to_instance(&db);
let int_ty = KnownClass::Int.to_instance(&db);
let object_ty = KnownClass::Object.to_instance(&db);
let unknown_ty = Type::Unknown;
let u0 = UnionType::from_elements(&db, [str_ty, unknown_ty, int_ty, object_ty]);

View File

@@ -36,9 +36,8 @@ impl Display for DisplayType<'_> {
| Type::BytesLiteral(_)
| Type::Class(_)
| Type::Function(_)
| Type::RevealTypeFunction(_)
) {
write!(f, "Literal[{representation}]",)
write!(f, "Literal[{representation}]")
} else {
representation.fmt(f)
}
@@ -67,15 +66,16 @@ impl Display for DisplayRepresentation<'_> {
Type::Unknown => f.write_str("Unknown"),
Type::Unbound => f.write_str("Unbound"),
Type::None => f.write_str("None"),
// `[Type::Todo]`'s display should be explicit that is not a valid display of
// any other type
Type::Todo => f.write_str("@Todo"),
Type::Module(file) => {
write!(f, "<module '{:?}'>", file.path(self.db))
}
// TODO functions and classes should display using a fully qualified name
Type::Class(class) => f.write_str(class.name(self.db)),
Type::Instance(class) => f.write_str(class.name(self.db)),
Type::Function(function) | Type::RevealTypeFunction(function) => {
f.write_str(function.name(self.db))
}
Type::Function(function) => f.write_str(function.name(self.db)),
Type::Union(union) => union.display(self.db).fmt(f),
Type::Intersection(intersection) => intersection.display(self.db).fmt(f),
Type::IntLiteral(n) => n.fmt(f),
@@ -194,7 +194,7 @@ impl TryFrom<Type<'_>> for LiteralTypeKind {
fn try_from(value: Type<'_>) -> Result<Self, Self::Error> {
match value {
Type::Class(_) => Ok(Self::Class),
Type::Function(_) | Type::RevealTypeFunction(_) => Ok(Self::Function),
Type::Function(_) => Ok(Self::Function),
Type::IntLiteral(_) => Ok(Self::IntLiteral),
Type::StringLiteral(_) => Ok(Self::StringLiteral),
Type::BytesLiteral(_) => Ok(Self::BytesLiteral),
@@ -335,18 +335,18 @@ mod tests {
class B: ...
",
)?;
let mod_file = system_path_to_file(&db, "src/main.py").expect("Expected file to exist.");
let mod_file = system_path_to_file(&db, "src/main.py").expect("file to exist");
let union_elements = &[
Type::Unknown,
Type::IntLiteral(-1),
global_symbol_ty(&db, mod_file, "A"),
Type::StringLiteral(StringLiteralType::new(&db, Box::from("A"))),
Type::BytesLiteral(BytesLiteralType::new(&db, Box::from([0]))),
Type::BytesLiteral(BytesLiteralType::new(&db, Box::from([7]))),
Type::StringLiteral(StringLiteralType::new(&db, "A")),
Type::BytesLiteral(BytesLiteralType::new(&db, [0u8].as_slice())),
Type::BytesLiteral(BytesLiteralType::new(&db, [7u8].as_slice())),
Type::IntLiteral(0),
Type::IntLiteral(1),
Type::StringLiteral(StringLiteralType::new(&db, Box::from("B"))),
Type::StringLiteral(StringLiteralType::new(&db, "B")),
global_symbol_ty(&db, mod_file, "foo"),
global_symbol_ty(&db, mod_file, "bar"),
global_symbol_ty(&db, mod_file, "B"),

File diff suppressed because it is too large Load Diff

View File

@@ -155,13 +155,24 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
let inference = infer_expression_types(self.db, expression);
for (op, comparator) in std::iter::zip(&**ops, &**comparators) {
let comp_ty = inference.expression_ty(comparator.scoped_ast_id(self.db, scope));
if matches!(op, ast::CmpOp::IsNot) {
let ty = IntersectionBuilder::new(self.db)
.add_negative(comp_ty)
.build();
self.constraints.insert(symbol, ty);
};
// TODO other comparison types
match op {
ast::CmpOp::IsNot => {
if comp_ty.is_singleton(self.db) {
let ty = IntersectionBuilder::new(self.db)
.add_negative(comp_ty)
.build();
self.constraints.insert(symbol, ty);
} else {
// Non-singletons cannot be safely narrowed using `is not`
}
}
ast::CmpOp::Is => {
self.constraints.insert(symbol, comp_ty);
}
_ => {
// TODO other comparison types
}
}
}
}
}

View File

@@ -0,0 +1,14 @@
use red_knot_test::run;
use std::path::PathBuf;
/// See `crates/red_knot_test/README.md` for documentation on these tests.
#[rstest::rstest]
fn mdtest(#[files("resources/mdtest/**/*.md")] path: PathBuf) {
let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("resources")
.join("mdtest")
.canonicalize()
.unwrap();
let title = path.strip_prefix(crate_dir).unwrap();
run(&path, title.as_os_str().to_str().unwrap());
}

View File

@@ -1 +0,0 @@
9e506eb5e8fc2823db8c60ad561b1145ff114947

View File

@@ -1,42 +0,0 @@
import sys
from collections.abc import Callable
from typing import Any, Generic, TypeVar, final, overload
from typing_extensions import Self
if sys.version_info >= (3, 9):
from types import GenericAlias
_C = TypeVar("_C", bound=Callable[..., Any])
_T = TypeVar("_T")
@final
class CallableProxyType(Generic[_C]): # "weakcallableproxy"
def __eq__(self, value: object, /) -> bool: ...
def __getattr__(self, attr: str) -> Any: ...
__call__: _C
@final
class ProxyType(Generic[_T]): # "weakproxy"
def __eq__(self, value: object, /) -> bool: ...
def __getattr__(self, attr: str) -> Any: ...
class ReferenceType(Generic[_T]):
__callback__: Callable[[Self], Any]
def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ...
def __init__(self, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> None: ...
def __call__(self) -> _T | None: ...
def __eq__(self, value: object, /) -> bool: ...
def __hash__(self) -> int: ...
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
ref = ReferenceType
def getweakrefcount(object: Any, /) -> int: ...
def getweakrefs(object: Any, /) -> list[Any]: ...
# Return CallableProxyType if object is callable, ProxyType otherwise
@overload
def proxy(object: _C, callback: Callable[[_C], Any] | None = None, /) -> CallableProxyType[_C]: ...
@overload
def proxy(object: _T, callback: Callable[[_T], Any] | None = None, /) -> Any: ...

View File

@@ -1,371 +0,0 @@
import os
import sys
from _ast import *
from _typeshed import ReadableBuffer, Unused
from collections.abc import Iterator
from typing import Any, Literal, TypeVar as _TypeVar, overload
from typing_extensions import deprecated
class _ABC(type):
if sys.version_info >= (3, 9):
def __init__(cls, *args: Unused) -> None: ...
if sys.version_info < (3, 14):
@deprecated("Replaced by ast.Constant; removed in Python 3.14")
class Num(Constant, metaclass=_ABC):
value: int | float | complex
@deprecated("Replaced by ast.Constant; removed in Python 3.14")
class Str(Constant, metaclass=_ABC):
value: str
# Aliases for value, for backwards compatibility
s: str
@deprecated("Replaced by ast.Constant; removed in Python 3.14")
class Bytes(Constant, metaclass=_ABC):
value: bytes
# Aliases for value, for backwards compatibility
s: bytes
@deprecated("Replaced by ast.Constant; removed in Python 3.14")
class NameConstant(Constant, metaclass=_ABC): ...
@deprecated("Replaced by ast.Constant; removed in Python 3.14")
class Ellipsis(Constant, metaclass=_ABC): ...
if sys.version_info >= (3, 9):
class slice(AST): ...
class ExtSlice(slice): ...
class Index(slice): ...
class Suite(mod): ...
class AugLoad(expr_context): ...
class AugStore(expr_context): ...
class Param(expr_context): ...
class NodeVisitor:
def visit(self, node: AST) -> Any: ...
def generic_visit(self, node: AST) -> Any: ...
def visit_Module(self, node: Module) -> Any: ...
def visit_Interactive(self, node: Interactive) -> Any: ...
def visit_Expression(self, node: Expression) -> Any: ...
def visit_FunctionDef(self, node: FunctionDef) -> Any: ...
def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any: ...
def visit_ClassDef(self, node: ClassDef) -> Any: ...
def visit_Return(self, node: Return) -> Any: ...
def visit_Delete(self, node: Delete) -> Any: ...
def visit_Assign(self, node: Assign) -> Any: ...
def visit_AugAssign(self, node: AugAssign) -> Any: ...
def visit_AnnAssign(self, node: AnnAssign) -> Any: ...
def visit_For(self, node: For) -> Any: ...
def visit_AsyncFor(self, node: AsyncFor) -> Any: ...
def visit_While(self, node: While) -> Any: ...
def visit_If(self, node: If) -> Any: ...
def visit_With(self, node: With) -> Any: ...
def visit_AsyncWith(self, node: AsyncWith) -> Any: ...
def visit_Raise(self, node: Raise) -> Any: ...
def visit_Try(self, node: Try) -> Any: ...
def visit_Assert(self, node: Assert) -> Any: ...
def visit_Import(self, node: Import) -> Any: ...
def visit_ImportFrom(self, node: ImportFrom) -> Any: ...
def visit_Global(self, node: Global) -> Any: ...
def visit_Nonlocal(self, node: Nonlocal) -> Any: ...
def visit_Expr(self, node: Expr) -> Any: ...
def visit_Pass(self, node: Pass) -> Any: ...
def visit_Break(self, node: Break) -> Any: ...
def visit_Continue(self, node: Continue) -> Any: ...
def visit_Slice(self, node: Slice) -> Any: ...
def visit_BoolOp(self, node: BoolOp) -> Any: ...
def visit_BinOp(self, node: BinOp) -> Any: ...
def visit_UnaryOp(self, node: UnaryOp) -> Any: ...
def visit_Lambda(self, node: Lambda) -> Any: ...
def visit_IfExp(self, node: IfExp) -> Any: ...
def visit_Dict(self, node: Dict) -> Any: ...
def visit_Set(self, node: Set) -> Any: ...
def visit_ListComp(self, node: ListComp) -> Any: ...
def visit_SetComp(self, node: SetComp) -> Any: ...
def visit_DictComp(self, node: DictComp) -> Any: ...
def visit_GeneratorExp(self, node: GeneratorExp) -> Any: ...
def visit_Await(self, node: Await) -> Any: ...
def visit_Yield(self, node: Yield) -> Any: ...
def visit_YieldFrom(self, node: YieldFrom) -> Any: ...
def visit_Compare(self, node: Compare) -> Any: ...
def visit_Call(self, node: Call) -> Any: ...
def visit_FormattedValue(self, node: FormattedValue) -> Any: ...
def visit_JoinedStr(self, node: JoinedStr) -> Any: ...
def visit_Constant(self, node: Constant) -> Any: ...
def visit_NamedExpr(self, node: NamedExpr) -> Any: ...
def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ...
def visit_Attribute(self, node: Attribute) -> Any: ...
def visit_Subscript(self, node: Subscript) -> Any: ...
def visit_Starred(self, node: Starred) -> Any: ...
def visit_Name(self, node: Name) -> Any: ...
def visit_List(self, node: List) -> Any: ...
def visit_Tuple(self, node: Tuple) -> Any: ...
def visit_Del(self, node: Del) -> Any: ...
def visit_Load(self, node: Load) -> Any: ...
def visit_Store(self, node: Store) -> Any: ...
def visit_And(self, node: And) -> Any: ...
def visit_Or(self, node: Or) -> Any: ...
def visit_Add(self, node: Add) -> Any: ...
def visit_BitAnd(self, node: BitAnd) -> Any: ...
def visit_BitOr(self, node: BitOr) -> Any: ...
def visit_BitXor(self, node: BitXor) -> Any: ...
def visit_Div(self, node: Div) -> Any: ...
def visit_FloorDiv(self, node: FloorDiv) -> Any: ...
def visit_LShift(self, node: LShift) -> Any: ...
def visit_Mod(self, node: Mod) -> Any: ...
def visit_Mult(self, node: Mult) -> Any: ...
def visit_MatMult(self, node: MatMult) -> Any: ...
def visit_Pow(self, node: Pow) -> Any: ...
def visit_RShift(self, node: RShift) -> Any: ...
def visit_Sub(self, node: Sub) -> Any: ...
def visit_Invert(self, node: Invert) -> Any: ...
def visit_Not(self, node: Not) -> Any: ...
def visit_UAdd(self, node: UAdd) -> Any: ...
def visit_USub(self, node: USub) -> Any: ...
def visit_Eq(self, node: Eq) -> Any: ...
def visit_Gt(self, node: Gt) -> Any: ...
def visit_GtE(self, node: GtE) -> Any: ...
def visit_In(self, node: In) -> Any: ...
def visit_Is(self, node: Is) -> Any: ...
def visit_IsNot(self, node: IsNot) -> Any: ...
def visit_Lt(self, node: Lt) -> Any: ...
def visit_LtE(self, node: LtE) -> Any: ...
def visit_NotEq(self, node: NotEq) -> Any: ...
def visit_NotIn(self, node: NotIn) -> Any: ...
def visit_comprehension(self, node: comprehension) -> Any: ...
def visit_ExceptHandler(self, node: ExceptHandler) -> Any: ...
def visit_arguments(self, node: arguments) -> Any: ...
def visit_arg(self, node: arg) -> Any: ...
def visit_keyword(self, node: keyword) -> Any: ...
def visit_alias(self, node: alias) -> Any: ...
def visit_withitem(self, node: withitem) -> Any: ...
if sys.version_info >= (3, 10):
def visit_Match(self, node: Match) -> Any: ...
def visit_match_case(self, node: match_case) -> Any: ...
def visit_MatchValue(self, node: MatchValue) -> Any: ...
def visit_MatchSequence(self, node: MatchSequence) -> Any: ...
def visit_MatchSingleton(self, node: MatchSingleton) -> Any: ...
def visit_MatchStar(self, node: MatchStar) -> Any: ...
def visit_MatchMapping(self, node: MatchMapping) -> Any: ...
def visit_MatchClass(self, node: MatchClass) -> Any: ...
def visit_MatchAs(self, node: MatchAs) -> Any: ...
def visit_MatchOr(self, node: MatchOr) -> Any: ...
if sys.version_info >= (3, 11):
def visit_TryStar(self, node: TryStar) -> Any: ...
if sys.version_info >= (3, 12):
def visit_TypeVar(self, node: TypeVar) -> Any: ...
def visit_ParamSpec(self, node: ParamSpec) -> Any: ...
def visit_TypeVarTuple(self, node: TypeVarTuple) -> Any: ...
def visit_TypeAlias(self, node: TypeAlias) -> Any: ...
# visit methods for deprecated nodes
def visit_ExtSlice(self, node: ExtSlice) -> Any: ...
def visit_Index(self, node: Index) -> Any: ...
def visit_Suite(self, node: Suite) -> Any: ...
def visit_AugLoad(self, node: AugLoad) -> Any: ...
def visit_AugStore(self, node: AugStore) -> Any: ...
def visit_Param(self, node: Param) -> Any: ...
def visit_Num(self, node: Num) -> Any: ...
def visit_Str(self, node: Str) -> Any: ...
def visit_Bytes(self, node: Bytes) -> Any: ...
def visit_NameConstant(self, node: NameConstant) -> Any: ...
def visit_Ellipsis(self, node: Ellipsis) -> Any: ...
class NodeTransformer(NodeVisitor):
def generic_visit(self, node: AST) -> AST: ...
# TODO: Override the visit_* methods with better return types.
# The usual return type is AST | None, but Iterable[AST]
# is also allowed in some cases -- this needs to be mapped.
_T = _TypeVar("_T", bound=AST)
if sys.version_info >= (3, 13):
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: Literal["exec"] = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Module: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["eval"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["func_type"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["single"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["eval"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["func_type"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["single"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: str = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
optimize: Literal[-1, 0, 1, 2] = -1,
) -> AST: ...
else:
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: Literal["exec"] = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Module: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["eval"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["func_type"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any],
mode: Literal["single"],
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["eval"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Expression: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["func_type"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> FunctionType: ...
@overload
def parse(
source: str | ReadableBuffer,
*,
mode: Literal["single"],
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> Interactive: ...
@overload
def parse(
source: str | ReadableBuffer,
filename: str | ReadableBuffer | os.PathLike[Any] = "<unknown>",
mode: str = "exec",
*,
type_comments: bool = False,
feature_version: None | int | tuple[int, int] = None,
) -> AST: ...
if sys.version_info >= (3, 9):
def unparse(ast_obj: AST) -> str: ...
def copy_location(new_node: _T, old_node: AST) -> _T: ...
if sys.version_info >= (3, 13):
def dump(
node: AST,
annotate_fields: bool = True,
include_attributes: bool = False,
*,
indent: int | str | None = None,
show_empty: bool = False,
) -> str: ...
elif sys.version_info >= (3, 9):
def dump(
node: AST, annotate_fields: bool = True, include_attributes: bool = False, *, indent: int | str | None = None
) -> str: ...
else:
def dump(node: AST, annotate_fields: bool = True, include_attributes: bool = False) -> str: ...
def fix_missing_locations(node: _T) -> _T: ...
def get_docstring(node: AsyncFunctionDef | FunctionDef | ClassDef | Module, clean: bool = True) -> str | None: ...
def increment_lineno(node: _T, n: int = 1) -> _T: ...
def iter_child_nodes(node: AST) -> Iterator[AST]: ...
def iter_fields(node: AST) -> Iterator[tuple[str, Any]]: ...
def literal_eval(node_or_string: str | AST) -> Any: ...
def get_source_segment(source: str, node: AST, *, padded: bool = False) -> str | None: ...
def walk(node: AST) -> Iterator[AST]: ...
if sys.version_info >= (3, 9):
def main() -> None: ...
if sys.version_info >= (3, 14):
def compare(left: AST, right: AST, /, *, compare_attributes: bool = False) -> bool: ...

View File

@@ -1,57 +0,0 @@
import sys
from collections.abc import Awaitable, Callable, Generator, Iterable
from concurrent.futures._base import Future as _ConcurrentFuture
from contextvars import Context
from typing import Any, Literal, TypeVar
from typing_extensions import Self, TypeIs
from .events import AbstractEventLoop
if sys.version_info >= (3, 9):
from types import GenericAlias
__all__ = ("Future", "wrap_future", "isfuture")
_T = TypeVar("_T")
# asyncio defines 'isfuture()' in base_futures.py and re-imports it in futures.py
# but it leads to circular import error in pytype tool.
# That's why the import order is reversed.
def isfuture(obj: object) -> TypeIs[Future[Any]]: ...
class Future(Awaitable[_T], Iterable[_T]):
_state: str
@property
def _exception(self) -> BaseException | None: ...
_blocking: bool
@property
def _log_traceback(self) -> bool: ...
@_log_traceback.setter
def _log_traceback(self, val: Literal[False]) -> None: ...
_asyncio_future_blocking: bool # is a part of duck-typing contract for `Future`
def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ...
def __del__(self) -> None: ...
def get_loop(self) -> AbstractEventLoop: ...
@property
def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ...
def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ...
if sys.version_info >= (3, 9):
def cancel(self, msg: Any | None = None) -> bool: ...
else:
def cancel(self) -> bool: ...
def cancelled(self) -> bool: ...
def done(self) -> bool: ...
def result(self) -> _T: ...
def exception(self) -> BaseException | None: ...
def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ...
def set_result(self, result: _T, /) -> None: ...
def set_exception(self, exception: type | BaseException, /) -> None: ...
def __iter__(self) -> Generator[Any, None, _T]: ...
def __await__(self) -> Generator[Any, None, _T]: ...
@property
def _loop(self) -> AbstractEventLoop: ...
if sys.version_info >= (3, 9):
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ...

View File

@@ -1,2 +0,0 @@
from _decimal import *
from _decimal import __libmpdec_version__ as __libmpdec_version__, __version__ as __version__

View File

@@ -1,40 +0,0 @@
from _typeshed import SupportsWrite
from email.message import Message
from email.policy import Policy
from typing_extensions import Self
__all__ = ["Generator", "DecodedGenerator", "BytesGenerator"]
class Generator:
def clone(self, fp: SupportsWrite[str]) -> Self: ...
def write(self, s: str) -> None: ...
def __init__(
self,
outfp: SupportsWrite[str],
mangle_from_: bool | None = None,
maxheaderlen: int | None = None,
*,
policy: Policy | None = None,
) -> None: ...
def flatten(self, msg: Message, unixfrom: bool = False, linesep: str | None = None) -> None: ...
class BytesGenerator(Generator):
def __init__(
self,
outfp: SupportsWrite[bytes],
mangle_from_: bool | None = None,
maxheaderlen: int | None = None,
*,
policy: Policy | None = None,
) -> None: ...
class DecodedGenerator(Generator):
def __init__(
self,
outfp: SupportsWrite[str],
mangle_from_: bool | None = None,
maxheaderlen: int | None = None,
fmt: str | None = None,
*,
policy: Policy | None = None,
) -> None: ...

View File

@@ -1,46 +0,0 @@
import os
import sys
from collections.abc import Iterator
from contextlib import AbstractContextManager
from pathlib import Path
from types import ModuleType
from typing import Any, BinaryIO, TextIO
from typing_extensions import TypeAlias
if sys.version_info >= (3, 9):
from importlib.abc import Traversable
__all__ = ["Package", "Resource", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"]
if sys.version_info >= (3, 9):
__all__ += ["as_file", "files"]
if sys.version_info >= (3, 10):
__all__ += ["ResourceReader"]
Package: TypeAlias = str | ModuleType
if sys.version_info >= (3, 11):
Resource: TypeAlias = str
else:
Resource: TypeAlias = str | os.PathLike[Any]
def open_binary(package: Package, resource: Resource) -> BinaryIO: ...
def open_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> TextIO: ...
def read_binary(package: Package, resource: Resource) -> bytes: ...
def read_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> str: ...
def path(package: Package, resource: Resource) -> AbstractContextManager[Path]: ...
def is_resource(package: Package, name: str) -> bool: ...
def contents(package: Package) -> Iterator[str]: ...
if sys.version_info >= (3, 9):
def as_file(path: Traversable) -> AbstractContextManager[Path]: ...
if sys.version_info >= (3, 12):
def files(anchor: Package | None = ...) -> Traversable: ...
elif sys.version_info >= (3, 9):
def files(package: Package) -> Traversable: ...
if sys.version_info >= (3, 10):
from importlib.abc import ResourceReader as ResourceReader

View File

@@ -1,113 +0,0 @@
import sys
from _operator import *
__all__ = [
"abs",
"add",
"and_",
"attrgetter",
"concat",
"contains",
"countOf",
"delitem",
"eq",
"floordiv",
"ge",
"getitem",
"gt",
"iadd",
"iand",
"iconcat",
"ifloordiv",
"ilshift",
"imatmul",
"imod",
"imul",
"index",
"indexOf",
"inv",
"invert",
"ior",
"ipow",
"irshift",
"is_",
"is_not",
"isub",
"itemgetter",
"itruediv",
"ixor",
"le",
"length_hint",
"lshift",
"lt",
"matmul",
"methodcaller",
"mod",
"mul",
"ne",
"neg",
"not_",
"or_",
"pos",
"pow",
"rshift",
"setitem",
"sub",
"truediv",
"truth",
"xor",
]
if sys.version_info >= (3, 11):
__all__ += ["call"]
if sys.version_info >= (3, 14):
__all__ += ["is_none", "is_not_none"]
__lt__ = lt
__le__ = le
__eq__ = eq
__ne__ = ne
__ge__ = ge
__gt__ = gt
__not__ = not_
__abs__ = abs
__add__ = add
__and__ = and_
__floordiv__ = floordiv
__index__ = index
__inv__ = inv
__invert__ = invert
__lshift__ = lshift
__mod__ = mod
__mul__ = mul
__matmul__ = matmul
__neg__ = neg
__or__ = or_
__pos__ = pos
__pow__ = pow
__rshift__ = rshift
__sub__ = sub
__truediv__ = truediv
__xor__ = xor
__concat__ = concat
__contains__ = contains
__delitem__ = delitem
__getitem__ = getitem
__setitem__ = setitem
__iadd__ = iadd
__iand__ = iand
__iconcat__ = iconcat
__ifloordiv__ = ifloordiv
__ilshift__ = ilshift
__imod__ = imod
__imul__ = imul
__imatmul__ = imatmul
__ior__ = ior
__ipow__ = ipow
__irshift__ = irshift
__isub__ = isub
__itruediv__ = itruediv
__ixor__ = ixor
if sys.version_info >= (3, 11):
__call__ = call

View File

@@ -1 +0,0 @@
from sqlite3.dbapi2 import *

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