Compare commits

..

63 Commits

Author SHA1 Message Date
Micha Reiser
da824ba316 Release Ruff 0.5.6 (#12629)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-08-02 17:35:14 +02:00
Micha Reiser
012198a1b0 Enable notebooks by default in preview mode (#12621) 2024-08-02 13:36:53 +00:00
Alex Waygood
fbab04fbe1 [red-knot] Allow multiple site-packages search paths (#12609) 2024-08-02 13:33:19 +00:00
Dhruv Manilawala
9aa43d5f91 Separate red_knot into CLI and red_knot_workspace crates (#12623)
## Summary

This PR separates the current `red_knot` crate into two crates:
1. `red_knot` - This will be similar to the `ruff` crate, it'll act as
the CLI crate
2. `red_knot_workspace` - This includes everything except for the CLI
functionality from the existing `red_knot` crate

Note that the code related to the file watcher is in
`red_knot_workspace` for now but might be required to extract it out in
the future.

The main motivation for this change is so that we can have a `red_knot
server` command. This makes it easier to test the server out without
making any changes in the VS Code extension. All we need is to specify
the `red_knot` executable path in `ruff.path` extension setting.

## Test Plan

- `cargo build`
- `cargo clippy --workspace --all-targets --all-features`
- `cargo shear --fix`
2024-08-02 11:24:36 +00:00
Micha Reiser
966563c79b Add tests for hard and soft links (#12590) 2024-08-02 10:14:28 +00:00
Micha Reiser
27edadec29 Make server panic hook more error resilient (#12610) 2024-08-02 12:10:06 +02:00
InSync
2e2b1b460f Fix a typo in docs/editors/settings.md (#12614)
Diff:

```diff
-- `false: Same as`off\`
+- `false`: Same as `off`
```
2024-08-01 11:23:55 -05:00
Charlie Marsh
a3e67abf4c Add newlines before comments in E305 (#12606)
## Summary

There's still a problem here. Given:

```python
class Class():
    pass

    # comment

    # another comment
a = 1
```

We only add one newline before `a = 1` on the first pass, because
`max_precedling_blank_lines` is 1... We then add the second newline on
the second pass, so it ends up in the right state, but the logic is
clearly wonky.

Closes https://github.com/astral-sh/ruff/issues/11508.
2024-07-31 23:11:00 -04:00
Carl Meyer
ee0518e8f7 [red-knot] implement attribute of union (#12601)
I hit this `todo!` trying to run type inference over some real modules.
Since it's a one-liner to implement it, I just did that rather than
changing to `Type::Unknown`.
2024-07-31 19:45:24 -07:00
Charlie Marsh
d774a3bd48 Avoid unused async when context manager includes TaskGroup (#12605)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12354.
2024-08-01 02:12:43 +00:00
Charlie Marsh
7e6b19048e Don't attach comments with mismatched indents (#12604)
## Summary

Given:

```python
def test_update():
    pass
    # comment
def test_clientmodel():
    pass
```

We don't want `# comment` to be attached to `def test_clientmodel()`.

Closes https://github.com/astral-sh/ruff/issues/12589.
2024-07-31 22:09:05 -04:00
Charlie Marsh
8e383b9587 Respect start index in unnecessary-list-index-lookup (#12603)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12594.
2024-08-01 01:21:15 +00:00
github-actions[bot]
3f49ab126f Sync vendored typeshed stubs (#12602) 2024-08-01 01:44:56 +01:00
Chris Krycho
c1bc7f4dee Remove ecosystem_ci flag from Ruff CLI (#12596)
## Summary

@zanieb noticed while we were discussing #12595 that this flag is now
unnecessary, so remove it and the flags which reference it.

## Test Plan

Question for maintainers: is there a test to add *or* remove here? (I’ve
opened this as a draft PR with that in view!)
2024-07-31 11:40:03 -05:00
Bowen Liang
a44d579f21 Add Dify to Ruff users (#12593)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
- Add the popular LLM Ops project Dify to the user list in Readme, as
Dify introduced Ruff for lining since Feb 2024 in
https://github.com/langgenius/dify/pull/2366
2024-07-31 08:56:52 -04:00
Alex Waygood
a3900d2b0b [pyflakes] Fix preview-mode bugs in F401 when attempting to autofix unused first-party submodule imports in an __init__.py file (#12569) 2024-07-31 13:34:30 +01:00
Alex Waygood
83b1c48a93 Make setting and retrieving pydocstyle settings less tedious (#12582) 2024-07-31 10:39:33 +01:00
Micha Reiser
138e70bd5c Upgrade to Rust 1.80 (#12586) 2024-07-30 19:18:08 +00:00
Eero Vaher
ee103ffb25 Fix an argument name in B905 description (#12588)
The description of `zip-without-explicit-strict` erroneously mentions a
non-existing `check` argument for `zip()`.
2024-07-30 14:40:56 -04:00
Micha Reiser
18f87b9497 Flaky file watching tests, add debug assertions (#12587) 2024-07-30 18:09:55 +00:00
Micha Reiser
adc8d4e1e7 File watch events: Add dynamic wait period before writing new changes (#12585) 2024-07-30 19:18:43 +02:00
Alex Waygood
90db361199 Consider more stdlib decorators to be property-like (#12583) 2024-07-30 17:18:23 +00:00
Alex Waygood
4738135801 Improve consistency between linter rules in determining whether a function is property (#12581) 2024-07-30 17:42:04 +01:00
Micha Reiser
264cd750e9 Add delay between updating a file (#12576) 2024-07-30 18:31:29 +02:00
Alex Waygood
7a4419a2a5 Improve handling of metaclasses in various linter rules (#12579) 2024-07-30 14:48:36 +01:00
Alex Waygood
ac1666d6e2 Remove several incorrect uses of map_callable() (#12580) 2024-07-30 14:30:25 +01:00
epenet
459c85ba27 [flake8-return] Exempt cached properties and other property-like decorators from explicit return rule (RET501) (#12563)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-07-30 11:06:28 +00:00
Alex Waygood
aaa56eb0bd Fix NFKC normalization bug when removing unused imports (#12571) 2024-07-30 09:54:35 +00:00
Dhruv Manilawala
f3c14a4276 Keep track of deleted cell for reorder change request (#12575)
## Summary

This PR fixes a bug where the server wouldn't retain the cell content in
case of a reorder change request.

As mentioned in
https://github.com/astral-sh/ruff/issues/12573#issuecomment-2257819298,
this change request is modeled as (a) remove these cell URIs and (b) add
these cell URIs. The cell content isn't provided. But, the way we've
modeled the `NotebookCell` (it contains the underlying `TextDocument`),
we need to keep track of the deleted cells to get the content.

This is not an ideal solution and a better long term solution would be
to model it as per the spec but that is a big structural change and will
affect multiple parts of the server. Modeling as per the spec would also
avoid bugs like https://github.com/astral-sh/ruff/pull/11864. For
context, that model would add complexity per
https://github.com/astral-sh/ruff/pull/11206#discussion_r1600165481.

fixes: #12573

## Test Plan

This video shows the before and after the bug is fixed:


https://github.com/user-attachments/assets/2fcad4b5-f9af-4776-8640-4cd1fa16e325
2024-07-30 09:51:26 +00:00
Alex Waygood
3169d408fa [red-knot] Fix typos in the module resolver (#12574) 2024-07-30 09:38:38 +00:00
Micha Reiser
a2286c8e47 Set Durability to 'HIGH' for most inputs and third-party libraries (#12566) 2024-07-30 09:03:59 +00:00
Piotr Osiewicz
fb9f566f56 Use $/logTrace for server trace logs in Zed and VS Code (#12564)
## Summary

This pull request adds support for logging via `$/logTrace` RPC
messages. It also enables that code path for when a client is Zed editor
or VS Code (as there's no way for us to generically tell whether a client prefers
`$/logTrace` over stderr.

Related to: #12523

## Test Plan

I've built Ruff from this branch and tested it manually with Zed.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2024-07-30 08:32:20 +05:30
Micha Reiser
381bd1ff4a Delete left over debug statement (#12567) 2024-07-29 16:16:12 +02:00
Micha Reiser
2f54d05d97 Remove salsa::report_untracked_read when finding the dynamic module resolution paths (#12509) 2024-07-29 09:31:29 +00:00
Micha Reiser
e18b4e42d3 [red-knot] Upgrade to the *new* *new* salsa (#12406) 2024-07-29 07:21:24 +00:00
Dhruv Manilawala
9495331a5f Recommend client config for trace setting in Neovim (#12562) 2024-07-29 06:14:34 +00:00
renovate[bot]
e1076db7d0 Update CodSpeedHQ/action action to v3 (#12559) 2024-07-29 07:37:02 +02:00
renovate[bot]
1986c9e8e2 Update NPM Development dependencies (#12556) 2024-07-28 22:17:44 -04:00
renovate[bot]
d7e80dc955 Update pre-commit dependencies (#12555) 2024-07-28 22:17:34 -04:00
renovate[bot]
87d09f77cd Update Rust crate imperative to v1.0.6 (#12552) 2024-07-28 22:17:28 -04:00
renovate[bot]
bd37ef13b8 Update Rust crate bstr to v1.10.0 (#12557) 2024-07-28 22:17:11 -04:00
renovate[bot]
ec23c974db Update Rust crate toml to v0.8.16 (#12554) 2024-07-28 22:17:01 -04:00
renovate[bot]
122e5ab428 Update Rust crate serde_json to v1.0.121 (#12553) 2024-07-28 22:16:55 -04:00
renovate[bot]
2f2149aca8 Update Rust crate env_logger to v0.11.5 (#12550) 2024-07-28 22:16:49 -04:00
renovate[bot]
9d5c31e7da Update Rust crate imara-diff to v0.1.7 (#12551) 2024-07-28 22:16:42 -04:00
renovate[bot]
25f3ad6238 Update Rust crate clap to v4.5.11 (#12549) 2024-07-28 22:16:36 -04:00
renovate[bot]
79926329a4 Update Rust crate argfile to v0.2.1 (#12548) 2024-07-28 22:16:31 -04:00
Aleksei Latyshev
9cdc578dd9 [flake8-builtins] Implement import, lambda, and module shadowing (#12546)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Extend `flake8-builtins` to imports, lambda-arguments, and modules to be
consistent with original checker
[flake8_builtins](https://github.com/gforcada/flake8-builtins/blob/main/flake8_builtins.py).

closes #12540 

## Details

- Implement builtin-import-shadowing (A004)
- Stop tracking imports shadowing in builtin-variable-shadowing (A001)
in preview mode.
- Implement builtin-lambda-argument-shadowing (A005)
- Implement builtin-module-shadowing (A006)
  - Add new option `linter.flake8_builtins.builtins_allowed_modules`

## Test Plan

cargo test
2024-07-29 01:42:42 +00:00
Charlie Marsh
665c75f7ab Add document for executable determination (#12547)
Closes https://github.com/astral-sh/ruff/issues/12505.
2024-07-28 16:23:00 -04:00
Micha Reiser
f37b39d6cc Allow downloading ecosystem results from forks (#12544) 2024-07-27 19:57:19 +02:00
Charlie Marsh
e18c45c310 Avoid marking required imports as unused (#12537)
## Summary

If an import is marked as "required", we should never flag it as unused.
In practice, this is rare, since required imports are typically used for
`__future__` annotations, which are always considered "used".

Closes https://github.com/astral-sh/ruff/issues/12458.
2024-07-26 14:23:43 -04:00
Charlie Marsh
d930052de8 Move required import parsing out of lint rule (#12536)
## Summary

Instead, make it part of the serialization and deserialization itself.
This makes it _much_ easier to reuse when solving
https://github.com/astral-sh/ruff/issues/12458.
2024-07-26 13:35:45 -04:00
Sigurd Spieckermann
7ad4df9e9f Complete FBT002 example with Enum argument (#12525)
## Summary

I've completed `FBT002` rule example with an `Enum` argument to show the
full usage in this case.
2024-07-26 11:50:19 -04:00
Charlie Marsh
425761e960 Use colon rather than dot formatting for integer-only types (#12534)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12421.
2024-07-26 15:48:19 +00:00
Carl Meyer
4b69271809 [red-knot] resolve int/list/dict/set/tuple to builtin type (#12521)
Now that we have builtins available, resolve some simple cases to the
right builtin type.

We should also adjust the display for types to include their module
name; that's not done yet here.
2024-07-26 08:21:31 -07:00
Micha Reiser
bf23d38a21 Remove unnecessary clone in workspace API (#12529) 2024-07-26 17:19:05 +02:00
Charlie Marsh
49f51583fa Always allow explicit multi-line concatenations when implicit are banned (#12532)
## Summary

Closes https://github.com/astral-sh/ruff/issues/11582.
2024-07-26 10:36:35 -04:00
Charlie Marsh
1fe4a5faed Avoid recommending __slots__ for classes that inherit from more than namedtuple (#12531)
## Summary

Closes https://github.com/astral-sh/ruff/issues/11887.
2024-07-26 14:24:40 +00:00
Charlie Marsh
998bfe0847 Avoid recommending no-argument super in slots=True dataclasses (#12530)
## Summary

Closes https://github.com/astral-sh/ruff/issues/12506.
2024-07-26 10:09:51 -04:00
Dhruv Manilawala
6f4db8675b [red-knot] Add support for untitled files (#12492)
## Summary

This PR adds support for untitled files in the Red Knot project.

Refer to the [design
discussion](https://github.com/astral-sh/ruff/discussions/12336) for
more details.

### Changes
* The `parsed_module` always assumes that the `SystemVirtual` path is of
`PySourceType::Python`.
* For the module resolver, as suggested, I went ahead by adding a new
`SystemOrVendoredPath` enum and renamed `FilePathRef` to
`SystemOrVendoredPathRef` (happy to consider better names here).
* The `file_to_module` query would return if it's a
`FilePath::SystemVirtual` variant because a virtual file doesn't belong
to any module.
* The sync implementation for the system virtual path is basically the
same as that of system path except that it uses the
`virtual_path_metadata`. The reason for this is that the system
(language server) would provide the metadata on whether it still exists
or not and if it exists, the corresponding metadata.

For point (1), VS Code would use `Untitled-1` for Python files and
`Untitled-1.ipynb` for Jupyter Notebooks. We could use this distinction
to determine whether the source type is `Python` or `Ipynb`.

## Test Plan

Added test cases in #12526
2024-07-26 18:13:31 +05:30
Micha Reiser
71f7aa4971 Remove criterion/codspeed compat layer (#12524) 2024-07-26 12:22:16 +02:00
Auguste Lalande
9f72f474e6 [pydoclint] Add docstring-missing-returns amd docstring-extraneous-returns (DOC201, DOC202) (#12485)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-07-26 06:36:00 +00:00
Carl Meyer
10c993e21a [red-knot] remove wrong __init__.py from file-watching tests (#12519) 2024-07-26 07:14:01 +01:00
631 changed files with 7042 additions and 3028 deletions

View File

@@ -616,10 +616,10 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: "Build benchmarks"
run: cargo codspeed build --features codspeed -p ruff_benchmark
run: cargo codspeed build -p ruff_benchmark
- name: "Run benchmarks"
uses: CodSpeedHQ/action@v2
uses: CodSpeedHQ/action@v3
with:
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}

View File

@@ -23,6 +23,7 @@ jobs:
name: pr-number
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
allow_forks: true
- name: Parse pull request number
id: pr-number
@@ -43,6 +44,7 @@ jobs:
path: pr/ecosystem
workflow_conclusion: completed
if_no_artifact_found: ignore
allow_forks: true
- name: Generate comment content
id: generate-comment

View File

@@ -10,7 +10,7 @@ exclude: |
crates/ruff_python_formatter/tests/snapshots/.*|
crates/ruff_python_resolver/resources/.*|
crates/ruff_python_resolver/tests/snapshots/.*|
crates/red_knot/resources/.*
crates/red_knot_workspace/resources/.*
)$
repos:
@@ -43,7 +43,7 @@ repos:
)$
- repo: https://github.com/crate-ci/typos
rev: v1.23.2
rev: v1.23.5
hooks:
- id: typos
@@ -57,7 +57,7 @@ repos:
pass_filenames: false # This makes it a lot faster
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.4
rev: v0.5.5
hooks:
- id: ruff-format
- id: ruff

View File

@@ -1,5 +1,55 @@
# Changelog
## 0.5.6
Ruff 0.5.6 automatically enables linting and formatting of notebooks in *preview mode*.
You can opt-out of this behavior by adding `*.ipynb` to the `extend-exclude` setting.
```toml
[tool.ruff]
extend-exclude = ["*.ipynb"]
```
### Preview features
- Enable notebooks by default in preview mode ([#12621](https://github.com/astral-sh/ruff/pull/12621))
- \[`flake8-builtins`\] Implement import, lambda, and module shadowing ([#12546](https://github.com/astral-sh/ruff/pull/12546))
- \[`pydoclint`\] Add `docstring-missing-returns` (`DOC201`) and `docstring-extraneous-returns` (`DOC202`) ([#12485](https://github.com/astral-sh/ruff/pull/12485))
### Rule changes
- \[`flake8-return`\] Exempt cached properties and other property-like decorators from explicit return rule (`RET501`) ([#12563](https://github.com/astral-sh/ruff/pull/12563))
### Server
- Make server panic hook more error resilient ([#12610](https://github.com/astral-sh/ruff/pull/12610))
- Use `$/logTrace` for server trace logs in Zed and VS Code ([#12564](https://github.com/astral-sh/ruff/pull/12564))
- Keep track of deleted cells for reorder change request ([#12575](https://github.com/astral-sh/ruff/pull/12575))
### Configuration
- \[`flake8-implicit-str-concat`\] Always allow explicit multi-line concatenations when implicit concatenations are banned ([#12532](https://github.com/astral-sh/ruff/pull/12532))
### Bug fixes
- \[`flake8-async`\] Avoid flagging `asyncio.timeout`s as unused when the context manager includes `asyncio.TaskGroup` ([#12605](https://github.com/astral-sh/ruff/pull/12605))
- \[`flake8-slots`\] Avoid recommending `__slots__` for classes that inherit from more than `namedtuple` ([#12531](https://github.com/astral-sh/ruff/pull/12531))
- \[`isort`\] Avoid marking required imports as unused ([#12537](https://github.com/astral-sh/ruff/pull/12537))
- \[`isort`\] Preserve trailing inline comments on import-from statements ([#12498](https://github.com/astral-sh/ruff/pull/12498))
- \[`pycodestyle`\] Add newlines before comments (`E305`) ([#12606](https://github.com/astral-sh/ruff/pull/12606))
- \[`pycodestyle`\] Don't attach comments with mismatched indents ([#12604](https://github.com/astral-sh/ruff/pull/12604))
- \[`pyflakes`\] Fix preview-mode bugs in `F401` when attempting to autofix unused first-party submodule imports in an `__init__.py` file ([#12569](https://github.com/astral-sh/ruff/pull/12569))
- \[`pylint`\] Respect start index in `unnecessary-list-index-lookup` ([#12603](https://github.com/astral-sh/ruff/pull/12603))
- \[`pyupgrade`\] Avoid recommending no-argument super in `slots=True` dataclasses ([#12530](https://github.com/astral-sh/ruff/pull/12530))
- \[`pyupgrade`\] Use colon rather than dot formatting for integer-only types ([#12534](https://github.com/astral-sh/ruff/pull/12534))
- Fix NFKC normalization bug when removing unused imports ([#12571](https://github.com/astral-sh/ruff/pull/12571))
### Other changes
- Consider more stdlib decorators to be property-like ([#12583](https://github.com/astral-sh/ruff/pull/12583))
- Improve handling of metaclasses in various linter rules ([#12579](https://github.com/astral-sh/ruff/pull/12579))
- Improve consistency between linter rules in determining whether a function is property ([#12581](https://github.com/astral-sh/ruff/pull/12581))
## 0.5.5
### Preview features

218
Cargo.lock generated
View File

@@ -141,9 +141,9 @@ checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "argfile"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c5c8e418080ef8aa932039d12eda7b6f5043baf48f1523c166fbc32d004534"
checksum = "0a1cc0ba69de57db40674c66f7cf2caee3981ddef084388482c95c0e2133e5e8"
dependencies = [
"fs-err",
"os_str_bytes",
@@ -189,10 +189,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bstr"
version = "1.9.1"
name = "boomphf"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
checksum = "617e2d952880a00583ddb9237ac3965732e8df6a92a8e7bcc054100ec467ec3b"
dependencies = [
"crossbeam-utils",
"log",
"rayon",
"wyhash",
]
[[package]]
name = "bstr"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [
"memchr",
"regex-automata 0.4.6",
@@ -314,9 +326,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.9"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
dependencies = [
"clap_builder",
"clap_derive",
@@ -324,9 +336,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.9"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
dependencies = [
"anstream",
"anstyle",
@@ -367,9 +379,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.8"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
dependencies = [
"heck",
"proc-macro2",
@@ -759,9 +771,9 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.11.3"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"anstream",
"anstyle",
@@ -930,9 +942,9 @@ dependencies = [
[[package]]
name = "hashlink"
version = "0.8.4"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown",
]
@@ -1021,9 +1033,9 @@ dependencies = [
[[package]]
name = "imara-diff"
version = "0.1.6"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af13c8ceb376860ff0c6a66d83a8cdd4ecd9e464da24621bbffcd02b49619434"
checksum = "fc9da1a252bd44cd341657203722352efc9bc0c847d06ea6d2dc1cd1135e0a01"
dependencies = [
"ahash",
"hashbrown",
@@ -1031,9 +1043,9 @@ dependencies = [
[[package]]
name = "imperative"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b70798296d538cdaa6d652941fcc795963f8b9878b9e300c9fab7a522bd2fc0"
checksum = "29a1f6526af721f9aec9ceed7ab8ebfca47f3399d08b80056c2acca3fcb694a9"
dependencies = [
"phf",
"rust-stemmers",
@@ -1526,10 +1538,84 @@ dependencies = [
]
[[package]]
name = "os_str_bytes"
version = "6.6.1"
name = "orx-concurrent-ordered-bag"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
checksum = "9aa866e2be4aa03927eddb481e7c479d5109fe3121324fb7db6d97f91adf9876"
dependencies = [
"orx-fixed-vec",
"orx-pinned-concurrent-col",
"orx-pinned-vec",
"orx-pseudo-default",
"orx-split-vec",
]
[[package]]
name = "orx-concurrent-vec"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5912426ffb660f8b61e8f0812a1d07400803cd5513969d2c7af4d69602ba8a1"
dependencies = [
"orx-concurrent-ordered-bag",
"orx-fixed-vec",
"orx-pinned-concurrent-col",
"orx-pinned-vec",
"orx-pseudo-default",
"orx-split-vec",
]
[[package]]
name = "orx-fixed-vec"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f69466c7c1fc2e1f00b58e39059b78c438b9fad144d1937ef177ecfc413e997"
dependencies = [
"orx-pinned-vec",
"orx-pseudo-default",
]
[[package]]
name = "orx-pinned-concurrent-col"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdbcb1fa05dc1676f1c9cf19f443b3d2d2ca5835911477d22fa77cad8b79208d"
dependencies = [
"orx-fixed-vec",
"orx-pinned-vec",
"orx-pseudo-default",
"orx-split-vec",
]
[[package]]
name = "orx-pinned-vec"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1071baf586de45722668234bddf56c52c1ece6a6153d16541bbb0505f0ac055"
dependencies = [
"orx-pseudo-default",
]
[[package]]
name = "orx-pseudo-default"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f627c439e723fa78e410a0faba89047a8a47d0dc013da5c0e05806e8a6cddb"
[[package]]
name = "orx-split-vec"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52b9dbfa8c7069ae73a890870d3aa9097a897d616751d3d0278f2b42d5214730"
dependencies = [
"orx-pinned-vec",
"orx-pseudo-default",
]
[[package]]
name = "os_str_bytes"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ac44c994af577c799b1b4bd80dc214701e349873ad894d6cdf96f4f7526e0b9"
dependencies = [
"memchr",
]
@@ -1858,13 +1944,11 @@ dependencies = [
"countme",
"crossbeam",
"ctrlc",
"notify",
"filetime",
"rayon",
"red_knot_module_resolver",
"red_knot_python_semantic",
"red_knot_workspace",
"ruff_db",
"ruff_python_ast",
"rustc-hash 2.0.0",
"salsa",
"tempfile",
"tracing",
@@ -1912,6 +1996,22 @@ dependencies = [
"tracing",
]
[[package]]
name = "red_knot_workspace"
version = "0.0.0"
dependencies = [
"anyhow",
"crossbeam",
"notify",
"red_knot_module_resolver",
"red_knot_python_semantic",
"ruff_db",
"ruff_python_ast",
"rustc-hash 2.0.0",
"salsa",
"tracing",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
@@ -1993,7 +2093,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.5.5"
version = "0.5.6"
dependencies = [
"anyhow",
"argfile",
@@ -2048,10 +2148,9 @@ name = "ruff_benchmark"
version = "0.0.0"
dependencies = [
"codspeed-criterion-compat",
"criterion",
"mimalloc",
"once_cell",
"red_knot",
"red_knot_workspace",
"ruff_db",
"ruff_linter",
"ruff_python_ast",
@@ -2088,6 +2187,8 @@ dependencies = [
"filetime",
"ignore",
"insta",
"matchit",
"path-slash",
"ruff_cache",
"ruff_notebook",
"ruff_python_ast",
@@ -2178,7 +2279,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.5.5"
version = "0.5.6"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.2",
@@ -2232,6 +2333,7 @@ dependencies = [
"thiserror",
"toml",
"typed-arena",
"unicode-normalization",
"unicode-width",
"unicode_names2",
"url",
@@ -2401,13 +2503,17 @@ version = "0.0.0"
dependencies = [
"bitflags 2.6.0",
"is-macro",
"ruff_cache",
"ruff_index",
"ruff_macros",
"ruff_python_ast",
"ruff_python_parser",
"ruff_python_stdlib",
"ruff_source_file",
"ruff_text_size",
"rustc-hash 2.0.0",
"schemars",
"serde",
]
[[package]]
@@ -2493,7 +2599,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.5.5"
version = "0.5.6"
dependencies = [
"console_error_panic_hook",
"console_log",
@@ -2540,6 +2646,7 @@ dependencies = [
"ruff_macros",
"ruff_python_ast",
"ruff_python_formatter",
"ruff_python_semantic",
"ruff_source_file",
"rustc-hash 2.0.0",
"schemars",
@@ -2632,25 +2739,34 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "salsa"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=a1bf3a613f451af7fc0a59411c56abc47fe8e8e1#a1bf3a613f451af7fc0a59411c56abc47fe8e8e1"
source = "git+https://github.com/MichaReiser/salsa.git?rev=0cae5c52a3240172ef0be5c9d19e63448c53397c#0cae5c52a3240172ef0be5c9d19e63448c53397c"
dependencies = [
"arc-swap",
"boomphf",
"crossbeam",
"dashmap 5.5.3",
"dashmap 6.0.1",
"hashlink",
"indexmap",
"log",
"orx-concurrent-vec",
"parking_lot",
"rustc-hash 1.1.0",
"rustc-hash 2.0.0",
"salsa-macro-rules",
"salsa-macros",
"smallvec",
"tracing",
]
[[package]]
name = "salsa-macro-rules"
version = "0.1.0"
source = "git+https://github.com/MichaReiser/salsa.git?rev=0cae5c52a3240172ef0be5c9d19e63448c53397c#0cae5c52a3240172ef0be5c9d19e63448c53397c"
[[package]]
name = "salsa-macros"
version = "0.18.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=a1bf3a613f451af7fc0a59411c56abc47fe8e8e1#a1bf3a613f451af7fc0a59411c56abc47fe8e8e1"
source = "git+https://github.com/MichaReiser/salsa.git?rev=0cae5c52a3240172ef0be5c9d19e63448c53397c#0cae5c52a3240172ef0be5c9d19e63448c53397c"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
@@ -2752,11 +2868,12 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.120"
version = "1.0.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
@@ -2774,9 +2891,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [
"serde",
]
@@ -3077,9 +3194,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.8.15"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
dependencies = [
"serde",
"serde_spanned",
@@ -3089,18 +3206,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.16"
version = "0.22.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
dependencies = [
"indexmap",
"serde",
@@ -3747,6 +3864,15 @@ version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wyhash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf6e163c25e3fac820b4b453185ea2dea3b6a3e0a721d4d23d75bd33734c295"
dependencies = [
"rand_core",
]
[[package]]
name = "yansi"
version = "0.5.1"

View File

@@ -4,7 +4,7 @@ resolver = "2"
[workspace.package]
edition = "2021"
rust-version = "1.75"
rust-version = "1.76"
homepage = "https://docs.astral.sh/ruff"
documentation = "https://docs.astral.sh/ruff"
repository = "https://github.com/astral-sh/ruff"
@@ -35,9 +35,9 @@ ruff_source_file = { path = "crates/ruff_source_file" }
ruff_text_size = { path = "crates/ruff_text_size" }
ruff_workspace = { path = "crates/ruff_workspace" }
red_knot = { path = "crates/red_knot" }
red_knot_module_resolver = { path = "crates/red_knot_module_resolver" }
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
red_knot_workspace = { path = "crates/red_knot_workspace" }
aho-corasick = { version = "1.1.3" }
annotate-snippets = { version = "0.9.2", features = ["color"] }
@@ -58,7 +58,6 @@ console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "1.0.0" }
countme = { version = "3.0.1" }
compact_str = "0.8.0"
criterion = { version = "0.5.1", default-features = false }
crossbeam = { version = "0.8.4" }
dashmap = { version = "6.0.1" }
drop_bomb = { version = "0.1.5" }
@@ -108,7 +107,7 @@ rand = { version = "0.8.5" }
rayon = { version = "1.10.0" }
regex = { version = "1.10.2" }
rustc-hash = { version = "2.0.0" }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a1bf3a613f451af7fc0a59411c56abc47fe8e8e1" }
salsa = { git = "https://github.com/MichaReiser/salsa.git", rev = "0cae5c52a3240172ef0be5c9d19e63448c53397c" }
schemars = { version = "0.8.16" }
seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }
@@ -157,6 +156,7 @@ zip = { version = "0.6.6", default-features = false, features = ["zstd"] }
[workspace.lints.rust]
unsafe_code = "warn"
unreachable_pub = "warn"
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -2 }

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.5.5/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.5.5/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.5.6/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.5.6/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.5.5
rev: v0.5.6
hooks:
# Run the linter.
- id: ruff
@@ -424,6 +424,7 @@ Ruff is used by a number of major open-source projects and companies, including:
- [Dagger](https://github.com/dagger/dagger)
- [Dagster](https://github.com/dagster-io/dagster)
- Databricks ([MLflow](https://github.com/mlflow/mlflow))
- [Dify](https://github.com/langgenius/dify)
- [FastAPI](https://github.com/tiangolo/fastapi)
- [Godot](https://github.com/godotengine/godot)
- [Gradio](https://github.com/gradio-app/gradio)

View File

@@ -10,4 +10,12 @@ doc-valid-idents = [
"SCREAMING_SNAKE_CASE",
"SQLAlchemy",
"StackOverflow",
"PyCharm",
]
ignore-interior-mutability = [
# Interned is read-only. The wrapped `Rc` never gets updated.
"ruff_formatter::format_element::Interned",
# The expression is read-only.
"ruff_python_ast::hashable::HashableExpr",
]

View File

@@ -13,25 +13,23 @@ license.workspace = true
[dependencies]
red_knot_module_resolver = { workspace = true }
red_knot_python_semantic = { workspace = true }
red_knot_workspace = { workspace = true }
ruff_db = { workspace = true, features = ["os", "cache"] }
ruff_python_ast = { workspace = true }
anyhow = { workspace = true }
clap = { workspace = true, features = ["wrap_help"] }
countme = { workspace = true, features = ["enable"] }
crossbeam = { workspace = true }
ctrlc = { version = "3.4.4" }
notify = { workspace = true }
rayon = { workspace = true }
rustc-hash = { workspace = true }
salsa = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
tracing-tree = { workspace = true }
[dev-dependencies]
filetime = { workspace = true }
tempfile = { workspace = true }

View File

@@ -2,7 +2,6 @@ use std::sync::Mutex;
use clap::Parser;
use crossbeam::channel as crossbeam_channel;
use salsa::ParallelDatabase;
use tracing::subscriber::Interest;
use tracing::{Level, Metadata};
use tracing_subscriber::filter::LevelFilter;
@@ -10,10 +9,10 @@ use tracing_subscriber::layer::{Context, Filter, SubscriberExt};
use tracing_subscriber::{Layer, Registry};
use tracing_tree::time::Uptime;
use red_knot::db::RootDatabase;
use red_knot::watch;
use red_knot::watch::WorkspaceWatcher;
use red_knot::workspace::WorkspaceMetadata;
use red_knot_workspace::db::RootDatabase;
use red_knot_workspace::watch;
use red_knot_workspace::watch::WorkspaceWatcher;
use red_knot_workspace::workspace::WorkspaceMetadata;
use ruff_db::program::{ProgramSettings, SearchPathSettings};
use ruff_db::system::{OsSystem, System, SystemPathBuf};
@@ -105,13 +104,13 @@ pub fn main() -> anyhow::Result<()> {
extra_paths,
workspace_root: workspace_metadata.root().to_path_buf(),
custom_typeshed: custom_typeshed_dir,
site_packages: None,
site_packages: vec![],
},
};
// TODO: Use the `program_settings` to compute the key for the database's persistent
// cache and load the cache if it exists.
let mut db = RootDatabase::new(workspace_metadata, program_settings, system);
let db = RootDatabase::new(workspace_metadata, program_settings, system);
let (main_loop, main_loop_cancellation_token) = MainLoop::new(verbosity);
@@ -125,11 +124,14 @@ pub fn main() -> anyhow::Result<()> {
}
})?;
let mut db = salsa::Handle::new(db);
if watch {
main_loop.watch(&mut db)?;
} else {
main_loop.run(&mut db);
}
};
std::mem::forget(db);
Ok(())
}
@@ -162,7 +164,7 @@ impl MainLoop {
)
}
fn watch(mut self, db: &mut RootDatabase) -> anyhow::Result<()> {
fn watch(mut self, db: &mut salsa::Handle<RootDatabase>) -> anyhow::Result<()> {
let sender = self.sender.clone();
let watcher = watch::directory_watcher(move |event| {
sender.send(MainLoopMessage::ApplyChanges(event)).unwrap();
@@ -170,12 +172,11 @@ impl MainLoop {
self.watcher = Some(WorkspaceWatcher::new(watcher, db));
self.run(db);
Ok(())
}
#[allow(clippy::print_stderr)]
fn run(mut self, db: &mut RootDatabase) {
fn run(mut self, db: &mut salsa::Handle<RootDatabase>) {
// Schedule the first check.
self.sender.send(MainLoopMessage::CheckWorkspace).unwrap();
let mut revision = 0usize;
@@ -185,7 +186,7 @@ impl MainLoop {
match message {
MainLoopMessage::CheckWorkspace => {
let db = db.snapshot();
let db = db.clone();
let sender = self.sender.clone();
// Spawn a new task that checks the workspace. This needs to be done in a separate thread
@@ -220,7 +221,7 @@ impl MainLoop {
MainLoopMessage::ApplyChanges(changes) => {
revision += 1;
// Automatically cancels any pending queries and waits for them to complete.
db.apply_changes(changes);
db.get_mut().apply_changes(changes);
if let Some(watcher) = self.watcher.as_mut() {
watcher.update(db);
}
@@ -231,6 +232,8 @@ impl MainLoop {
}
}
}
self.exit();
}
#[allow(clippy::print_stderr, clippy::unused_self)]
@@ -296,6 +299,9 @@ impl LoggingFilter {
fn is_enabled(&self, meta: &Metadata<'_>) -> bool {
let filter = if meta.target().starts_with("red_knot") || meta.target().starts_with("ruff") {
self.trace_level
} else if meta.target().starts_with("salsa") && self.trace_level <= Level::INFO {
// Salsa emits very verbose query traces with level info. Let's not show these to the user.
Level::WARN
} else {
Level::INFO
};

View File

@@ -1,15 +1,17 @@
#![allow(clippy::disallowed_names)]
use std::io::Write;
use std::time::Duration;
use anyhow::{anyhow, Context};
use salsa::Setter;
use red_knot::db::RootDatabase;
use red_knot::watch;
use red_knot::watch::{directory_watcher, WorkspaceWatcher};
use red_knot::workspace::WorkspaceMetadata;
use red_knot_module_resolver::{resolve_module, ModuleName};
use ruff_db::files::{system_path_to_file, File};
use red_knot_workspace::db::RootDatabase;
use red_knot_workspace::watch;
use red_knot_workspace::watch::{directory_watcher, WorkspaceWatcher};
use red_knot_workspace::workspace::WorkspaceMetadata;
use ruff_db::files::{system_path_to_file, File, FileError};
use ruff_db::program::{Program, ProgramSettings, SearchPathSettings, TargetVersion};
use ruff_db::source::source_text;
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
@@ -19,7 +21,10 @@ struct TestCase {
db: RootDatabase,
watcher: Option<WorkspaceWatcher>,
changes_receiver: crossbeam::channel::Receiver<Vec<watch::ChangeEvent>>,
temp_dir: tempfile::TempDir,
/// The temporary directory that contains the test files.
/// We need to hold on to it in the test case or the temp files get deleted.
_temp_dir: tempfile::TempDir,
root_dir: SystemPathBuf,
}
impl TestCase {
@@ -28,7 +33,7 @@ impl TestCase {
}
fn root_path(&self) -> &SystemPath {
SystemPath::from_std_path(self.temp_dir.path()).unwrap()
&self.root_dir
}
fn db(&self) -> &RootDatabase {
@@ -40,19 +45,63 @@ impl TestCase {
}
fn stop_watch(&mut self) -> Vec<watch::ChangeEvent> {
if let Some(watcher) = self.watcher.take() {
// Give the watcher some time to catch up.
std::thread::sleep(Duration::from_millis(10));
self.try_stop_watch(Duration::from_secs(10))
.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.");
let mut all_events = self
.changes_receiver
.recv_timeout(timeout)
.unwrap_or_default();
watcher.flush();
watcher.stop();
for event in &self.changes_receiver {
all_events.extend(event);
}
if all_events.is_empty() {
return None;
}
Some(all_events)
}
#[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.")
}
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
let Some(watcher) = &self.watcher else {
return None;
};
let mut all_events = self
.changes_receiver
.recv_timeout(timeout)
.unwrap_or_default();
watcher.flush();
while let Ok(event) = self
.changes_receiver
.recv_timeout(Duration::from_millis(10))
{
all_events.extend(event);
watcher.flush();
watcher.stop();
}
let mut all_events = Vec::new();
for events in &self.changes_receiver {
all_events.extend(events);
if all_events.is_empty() {
return None;
}
all_events
Some(all_events)
}
fn update_search_path_settings(
@@ -81,33 +130,67 @@ impl TestCase {
collected
}
fn system_file(&self, path: impl AsRef<SystemPath>) -> Option<File> {
fn system_file(&self, path: impl AsRef<SystemPath>) -> Result<File, FileError> {
system_path_to_file(self.db(), path.as_ref())
}
}
fn setup<I, P>(workspace_files: I) -> anyhow::Result<TestCase>
trait SetupFiles {
fn setup(self, root_path: &SystemPath, workspace_path: &SystemPath) -> anyhow::Result<()>;
}
impl<const N: usize, P> SetupFiles for [(P, &'static str); N]
where
I: IntoIterator<Item = (P, &'static str)>,
P: AsRef<SystemPath>,
{
setup_with_search_paths(workspace_files, |_root, workspace_path| {
SearchPathSettings {
extra_paths: vec![],
workspace_root: workspace_path.to_path_buf(),
custom_typeshed: None,
site_packages: None,
fn setup(self, _root_path: &SystemPath, workspace_path: &SystemPath) -> anyhow::Result<()> {
for (relative_path, content) in self {
let relative_path = relative_path.as_ref();
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}'.",)
})?;
}
let mut file = std::fs::File::create(absolute_path.as_std_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}'"))?;
file.sync_data()?;
}
Ok(())
}
}
impl<F> SetupFiles for F
where
F: FnOnce(&SystemPath, &SystemPath) -> anyhow::Result<()>,
{
fn setup(self, root_path: &SystemPath, workspace_path: &SystemPath) -> anyhow::Result<()> {
self(root_path, workspace_path)
}
}
fn setup<F>(setup_files: F) -> anyhow::Result<TestCase>
where
F: SetupFiles,
{
setup_with_search_paths(setup_files, |_root, workspace_path| SearchPathSettings {
extra_paths: vec![],
workspace_root: workspace_path.to_path_buf(),
custom_typeshed: None,
site_packages: vec![],
})
}
fn setup_with_search_paths<I, P>(
workspace_files: I,
fn setup_with_search_paths<F>(
setup_files: F,
create_search_paths: impl FnOnce(&SystemPath, &SystemPath) -> SearchPathSettings,
) -> anyhow::Result<TestCase>
where
I: IntoIterator<Item = (P, &'static str)>,
P: AsRef<SystemPath>,
F: SetupFiles,
{
let temp_dir = tempfile::tempdir()?;
@@ -130,18 +213,9 @@ where
std::fs::create_dir_all(workspace_path.as_std_path())
.with_context(|| format!("Failed to create workspace directory '{workspace_path}'",))?;
for (relative_path, content) in workspace_files {
let relative_path = relative_path.as_ref();
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}'.",)
})?;
}
std::fs::write(absolute_path.as_std_path(), content)
.with_context(|| format!("Failed to write file '{relative_path}'"))?;
}
setup_files
.setup(&root_path, &workspace_path)
.context("Failed to setup test files")?;
let system = OsSystem::new(&workspace_path);
@@ -176,12 +250,46 @@ where
db,
changes_receiver: receiver,
watcher: Some(watcher),
temp_dir,
_temp_dir: temp_dir,
root_dir: root_path,
};
// Sometimes the file watcher reports changes for events that happened before the watcher was started.
// Do a best effort at dropping these events.
test_case.try_take_watch_changes(Duration::from_millis(100));
Ok(test_case)
}
/// Updates the content of a file and ensures that the last modified file time is updated.
fn update_file(path: impl AsRef<SystemPath>, content: &str) -> anyhow::Result<()> {
let path = path.as_ref().as_std_path();
let metadata = path.metadata()?;
let last_modified_time = filetime::FileTime::from_last_modification_time(&metadata);
let mut file = std::fs::OpenOptions::new()
.create(false)
.write(true)
.truncate(true)
.open(path)?;
file.write_all(content.as_bytes())?;
loop {
file.sync_all()?;
let modified_time = filetime::FileTime::from_last_modification_time(&path.metadata()?);
if modified_time != last_modified_time {
break Ok(());
}
std::thread::sleep(Duration::from_nanos(10));
filetime::set_file_handle_times(&file, None, Some(filetime::FileTime::now()))?;
}
}
#[test]
fn new_file() -> anyhow::Result<()> {
let mut case = setup([("bar.py", "")])?;
@@ -189,7 +297,7 @@ fn new_file() -> anyhow::Result<()> {
let bar_file = case.system_file(&bar_path).unwrap();
let foo_path = case.workspace_path("foo.py");
assert_eq!(case.system_file(&foo_path), None);
assert_eq!(case.system_file(&foo_path), Err(FileError::NotFound));
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
@@ -212,7 +320,7 @@ fn new_ignored_file() -> anyhow::Result<()> {
let bar_file = case.system_file(&bar_path).unwrap();
let foo_path = case.workspace_path("foo.py");
assert_eq!(case.system_file(&foo_path), None);
assert_eq!(case.system_file(&foo_path), Err(FileError::NotFound));
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
@@ -221,7 +329,7 @@ fn new_ignored_file() -> anyhow::Result<()> {
case.db_mut().apply_changes(changes);
assert!(case.system_file(&foo_path).is_some());
assert!(case.system_file(&foo_path).is_ok());
assert_eq!(&case.collect_package_files(&bar_path), &[bar_file]);
Ok(())
@@ -233,16 +341,16 @@ fn changed_file() -> anyhow::Result<()> {
let mut case = setup([("foo.py", foo_source)])?;
let foo_path = case.workspace_path("foo.py");
let foo = case
.system_file(&foo_path)
.ok_or_else(|| anyhow!("Foo not found"))?;
let foo = case.system_file(&foo_path)?;
assert_eq!(source_text(case.db(), foo).as_str(), foo_source);
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
std::fs::write(foo_path.as_std_path(), "print('Version 2')")?;
update_file(&foo_path, "print('Version 2')")?;
let changes = case.stop_watch();
assert!(!changes.is_empty());
case.db_mut().apply_changes(changes);
assert_eq!(source_text(case.db(), foo).as_str(), "print('Version 2')");
@@ -251,59 +359,13 @@ fn changed_file() -> anyhow::Result<()> {
Ok(())
}
#[cfg(unix)]
#[test]
fn changed_metadata() -> anyhow::Result<()> {
use std::os::unix::fs::PermissionsExt;
let mut case = setup([("foo.py", "")])?;
let foo_path = case.workspace_path("foo.py");
let foo = case
.system_file(&foo_path)
.ok_or_else(|| anyhow!("Foo not found"))?;
assert_eq!(
foo.permissions(case.db()),
Some(
std::fs::metadata(foo_path.as_std_path())
.unwrap()
.permissions()
.mode()
)
);
std::fs::set_permissions(
foo_path.as_std_path(),
std::fs::Permissions::from_mode(0o777),
)
.with_context(|| "Failed to set file permissions.")?;
let changes = case.stop_watch();
case.db_mut().apply_changes(changes);
assert_eq!(
foo.permissions(case.db()),
Some(
std::fs::metadata(foo_path.as_std_path())
.unwrap()
.permissions()
.mode()
)
);
Ok(())
}
#[test]
fn deleted_file() -> anyhow::Result<()> {
let foo_source = "print('Hello, world!')";
let mut case = setup([("foo.py", foo_source)])?;
let foo_path = case.workspace_path("foo.py");
let foo = case
.system_file(&foo_path)
.ok_or_else(|| anyhow!("Foo not found"))?;
let foo = case.system_file(&foo_path)?;
assert!(foo.exists(case.db()));
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
@@ -332,9 +394,7 @@ fn move_file_to_trash() -> anyhow::Result<()> {
let trash_path = case.root_path().join(".trash");
std::fs::create_dir_all(trash_path.as_std_path())?;
let foo = case
.system_file(&foo_path)
.ok_or_else(|| anyhow!("Foo not found"))?;
let foo = case.system_file(&foo_path)?;
assert!(foo.exists(case.db()));
assert_eq!(&case.collect_package_files(&foo_path), &[foo]);
@@ -366,7 +426,7 @@ fn move_file_to_workspace() -> anyhow::Result<()> {
let foo_in_workspace_path = case.workspace_path("foo.py");
assert!(case.system_file(&foo_path).is_some());
assert!(case.system_file(&foo_path).is_ok());
assert_eq!(&case.collect_package_files(&bar_path), &[bar]);
assert!(case
.db()
@@ -380,9 +440,7 @@ fn move_file_to_workspace() -> anyhow::Result<()> {
case.db_mut().apply_changes(changes);
let foo_in_workspace = case
.system_file(&foo_in_workspace_path)
.ok_or_else(|| anyhow!("Foo not found"))?;
let foo_in_workspace = case.system_file(&foo_in_workspace_path)?;
assert!(foo_in_workspace.exists(case.db()));
assert_eq!(
@@ -400,9 +458,7 @@ fn rename_file() -> anyhow::Result<()> {
let foo_path = case.workspace_path("foo.py");
let bar_path = case.workspace_path("bar.py");
let foo = case
.system_file(&foo_path)
.ok_or_else(|| anyhow!("Foo not found"))?;
let foo = case.system_file(&foo_path)?;
assert_eq!(case.collect_package_files(&foo_path), [foo]);
@@ -414,9 +470,7 @@ fn rename_file() -> anyhow::Result<()> {
assert!(!foo.exists(case.db()));
let bar = case
.system_file(&bar_path)
.ok_or_else(|| anyhow!("Bar not found"))?;
let bar = case.system_file(&bar_path)?;
assert!(bar.exists(case.db()));
assert_eq!(case.collect_package_files(&foo_path), [bar]);
@@ -482,7 +536,7 @@ 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
@@ -643,7 +697,7 @@ fn search_path() -> anyhow::Result<()> {
extra_paths: vec![],
workspace_root: workspace_path.to_path_buf(),
custom_typeshed: None,
site_packages: Some(root_path.join("site_packages")),
site_packages: vec![root_path.join("site_packages")],
}
})?;
@@ -655,7 +709,6 @@ fn search_path() -> anyhow::Result<()> {
);
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
std::fs::write(site_packages.join("__init__.py").as_std_path(), "")?;
let changes = case.stop_watch();
@@ -681,12 +734,11 @@ fn add_search_path() -> anyhow::Result<()> {
// Register site-packages as a search path.
case.update_search_path_settings(|settings| SearchPathSettings {
site_packages: Some(site_packages.clone()),
site_packages: vec![site_packages.clone()],
..settings.clone()
});
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
std::fs::write(site_packages.join("__init__.py").as_std_path(), "")?;
let changes = case.stop_watch();
@@ -705,22 +757,477 @@ fn remove_search_path() -> anyhow::Result<()> {
extra_paths: vec![],
workspace_root: workspace_path.to_path_buf(),
custom_typeshed: None,
site_packages: Some(root_path.join("site_packages")),
site_packages: vec![root_path.join("site_packages")],
}
})?;
// Remove site packages from the search path settings.
let site_packages = case.root_path().join("site_packages");
case.update_search_path_settings(|settings| SearchPathSettings {
site_packages: None,
site_packages: vec![],
..settings.clone()
});
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
let changes = case.stop_watch();
let changes = case.try_stop_watch(Duration::from_millis(100));
assert_eq!(changes, &[]);
assert_eq!(changes, None);
Ok(())
}
/// Watch a workspace that contains two files where one file is a hardlink to another.
///
/// Setup:
/// ```text
/// - workspace
/// |- foo.py
/// |- bar.py (hard link to foo.py)
/// ```
///
/// # Linux
/// `inotify` only emits a single change event for the file that was changed.
/// Other files that point to the same inode (hardlinks) won't get updated.
///
/// For reference: VS Code and PyCharm have the same behavior where the results for one of the
/// files are stale.
///
/// # Windows
/// I haven't found any documentation that states the notification behavior on Windows but what
/// we're seeing is that Windows only emits a single event, similar to Linux.
#[test]
fn hard_links_in_workspace() -> anyhow::Result<()> {
let mut case = setup(|_root: &SystemPath, workspace: &SystemPath| {
let foo_path = workspace.join("foo.py");
std::fs::write(foo_path.as_std_path(), "print('Version 1')")?;
// Create a hardlink to `foo`
let bar_path = workspace.join("bar.py");
std::fs::hard_link(foo_path.as_std_path(), bar_path.as_std_path())
.context("Failed to create hard link from foo.py -> bar.py")?;
Ok(())
})?;
let foo_path = case.workspace_path("foo.py");
let foo = case.system_file(&foo_path).unwrap();
let bar_path = case.workspace_path("bar.py");
let bar = case.system_file(&bar_path).unwrap();
assert_eq!(source_text(case.db(), foo).as_str(), "print('Version 1')");
assert_eq!(source_text(case.db(), bar).as_str(), "print('Version 1')");
// Write to the hard link target.
update_file(foo_path, "print('Version 2')").context("Failed to update foo.py")?;
let changes = case.stop_watch();
case.db_mut().apply_changes(changes);
assert_eq!(source_text(case.db(), foo).as_str(), "print('Version 2')");
// macOS is the only platform that emits events for every hardlink.
if cfg!(target_os = "macos") {
assert_eq!(source_text(case.db(), bar).as_str(), "print('Version 2')");
}
Ok(())
}
/// Watch a workspace that contains one file that is a hardlink to a file outside the workspace.
///
/// Setup:
/// ```text
/// - foo.py
/// - workspace
/// |- bar.py (hard link to /foo.py)
/// ```
///
/// # Linux
/// inotiyf doesn't support observing changes to hard linked files.
///
/// > Note: when monitoring a directory, events are not generated for
/// > the files inside the directory when the events are performed via
/// > a pathname (i.e., a link) that lies outside the monitored
/// > directory. [source](https://man7.org/linux/man-pages/man7/inotify.7.html)
///
/// # Windows
/// > Retrieves information that describes the changes within the specified directory.
///
/// [source](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw)
///
/// My interpretation of this is that Windows doesn't support observing changes made to
/// hard linked files outside the workspace.
#[test]
#[cfg_attr(
target_os = "linux",
ignore = "inotify doesn't support observing changes to hard linked files."
)]
#[cfg_attr(
target_os = "windows",
ignore = "windows doesn't support observing changes to hard linked files."
)]
fn hard_links_to_target_outside_workspace() -> anyhow::Result<()> {
let mut case = setup(|root: &SystemPath, workspace: &SystemPath| {
let foo_path = root.join("foo.py");
std::fs::write(foo_path.as_std_path(), "print('Version 1')")?;
// Create a hardlink to `foo`
let bar_path = workspace.join("bar.py");
std::fs::hard_link(foo_path.as_std_path(), bar_path.as_std_path())
.context("Failed to create hard link from foo.py -> bar.py")?;
Ok(())
})?;
let foo_path = case.root_path().join("foo.py");
let foo = case.system_file(&foo_path).unwrap();
let bar_path = case.workspace_path("bar.py");
let bar = case.system_file(&bar_path).unwrap();
assert_eq!(source_text(case.db(), foo).as_str(), "print('Version 1')");
assert_eq!(source_text(case.db(), bar).as_str(), "print('Version 1')");
// Write to the hard link target.
update_file(foo_path, "print('Version 2')").context("Failed to update foo.py")?;
let changes = case.stop_watch();
case.db_mut().apply_changes(changes);
assert_eq!(source_text(case.db(), bar).as_str(), "print('Version 2')");
Ok(())
}
#[cfg(unix)]
mod unix {
//! Tests that make use of unix specific file-system features.
use super::*;
/// Changes the metadata of the only file in the workspace.
#[test]
fn changed_metadata() -> anyhow::Result<()> {
use std::os::unix::fs::PermissionsExt;
let mut case = setup([("foo.py", "")])?;
let foo_path = case.workspace_path("foo.py");
let foo = case.system_file(&foo_path)?;
assert_eq!(
foo.permissions(case.db()),
Some(
std::fs::metadata(foo_path.as_std_path())
.unwrap()
.permissions()
.mode()
)
);
std::fs::set_permissions(
foo_path.as_std_path(),
std::fs::Permissions::from_mode(0o777),
)
.with_context(|| "Failed to set file permissions.")?;
let changes = case.stop_watch();
case.db_mut().apply_changes(changes);
assert_eq!(
foo.permissions(case.db()),
Some(
std::fs::metadata(foo_path.as_std_path())
.unwrap()
.permissions()
.mode()
)
);
Ok(())
}
/// A workspace path is a symlink to a file outside the workspace.
///
/// Setup:
/// ```text
/// - bar
/// |- baz.py
///
/// - workspace
/// |- bar -> /bar
/// ```
///
/// # macOS
/// This test case isn't supported on macOS.
/// macOS uses `FSEvents` and `FSEvents` doesn't emit an event if a file in a symlinked directory is changed.
///
/// > Generally speaking, when working with file system event notifications, you will probably want to use lstat,
/// > because changes to the underlying file will not result in a change notification for the directory containing
/// > the symbolic link to that file. However, if you are working with a controlled file structure in
/// > which symbolic links always point within your watched tree, you might have reason to use stat.
///
/// [source](https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html#//apple_ref/doc/uid/TP40005289-CH4-SW4)
///
/// Pyright also does not support this case.
#[test]
#[cfg_attr(
target_os = "macos",
ignore = "FSEvents doesn't emit change events for symlinked directories outside of the watched paths."
)]
fn symlink_target_outside_watched_paths() -> anyhow::Result<()> {
let mut case = setup(|root: &SystemPath, workspace: &SystemPath| {
// Set up the symlink target.
let link_target = root.join("bar");
std::fs::create_dir_all(link_target.as_std_path())
.context("Failed to create link target directory")?;
let baz_original = link_target.join("baz.py");
std::fs::write(baz_original.as_std_path(), "def baz(): ...")
.context("Failed to write link target file")?;
// Create a symlink inside the workspace
let bar = workspace.join("bar");
std::os::unix::fs::symlink(link_target.as_std_path(), bar.as_std_path())
.context("Failed to create symlink to bar package")?;
Ok(())
})?;
let baz = resolve_module(
case.db().upcast(),
ModuleName::new_static("bar.baz").unwrap(),
)
.expect("Expected bar.baz to exist in site-packages.");
let baz_workspace = case.workspace_path("bar/baz.py");
assert_eq!(
source_text(case.db(), baz.file()).as_str(),
"def baz(): ..."
);
assert_eq!(
baz.file().path(case.db()).as_system_path(),
Some(&*baz_workspace)
);
let baz_original = case.root_path().join("bar/baz.py");
// Write to the symlink target.
update_file(baz_original, "def baz(): print('Version 2')")
.context("Failed to update bar/baz.py")?;
let changes = case.take_watch_changes();
case.db_mut().apply_changes(changes);
assert_eq!(
source_text(case.db(), baz.file()).as_str(),
"def baz(): print('Version 2')"
);
// Write to the symlink source.
update_file(baz_workspace, "def baz(): print('Version 3')")
.context("Failed to update bar/baz.py")?;
let changes = case.stop_watch();
case.db_mut().apply_changes(changes);
assert_eq!(
source_text(case.db(), baz.file()).as_str(),
"def baz(): print('Version 3')"
);
Ok(())
}
/// Workspace contains a symlink to another directory inside the workspace.
/// Changes to files in the symlinked directory should be reflected
/// to all files.
///
/// Setup:
/// ```text
/// - workspace
/// | - bar -> /workspace/patched/bar
/// |
/// | - patched
/// | |-- bar
/// | | |- baz.py
/// |
/// |-- foo.py
/// ```
#[test]
fn symlink_inside_workspace() -> anyhow::Result<()> {
let mut case = setup(|_root: &SystemPath, workspace: &SystemPath| {
// Set up the symlink target.
let link_target = workspace.join("patched/bar");
std::fs::create_dir_all(link_target.as_std_path())
.context("Failed to create link target directory")?;
let baz_original = link_target.join("baz.py");
std::fs::write(baz_original.as_std_path(), "def baz(): ...")
.context("Failed to write link target file")?;
// Create a symlink inside site-packages
let bar_in_workspace = workspace.join("bar");
std::os::unix::fs::symlink(link_target.as_std_path(), bar_in_workspace.as_std_path())
.context("Failed to create symlink to bar package")?;
Ok(())
})?;
let baz = resolve_module(
case.db().upcast(),
ModuleName::new_static("bar.baz").unwrap(),
)
.expect("Expected bar.baz to exist in site-packages.");
let bar_baz = case.workspace_path("bar/baz.py");
let patched_bar_baz = case.workspace_path("patched/bar/baz.py");
let patched_bar_baz_file = case.system_file(&patched_bar_baz).unwrap();
assert_eq!(
source_text(case.db(), patched_bar_baz_file).as_str(),
"def baz(): ..."
);
assert_eq!(
source_text(case.db(), baz.file()).as_str(),
"def baz(): ..."
);
assert_eq!(baz.file().path(case.db()).as_system_path(), Some(&*bar_baz));
// Write to the symlink target.
update_file(&patched_bar_baz, "def baz(): print('Version 2')")
.context("Failed to update bar/baz.py")?;
let changes = case.stop_watch();
case.db_mut().apply_changes(changes);
// The file watcher is guaranteed to emit one event for the changed file, but it isn't specified
// if the event is emitted for the "original" or linked path because both paths are watched.
// The best we can assert here is that one of the files should have been updated.
//
// In a perfect world, the file watcher would emit two events, one for the original file and
// one for the symlink. I tried parcel/watcher, node's `fs.watch` and `chokidar` and
// only `chokidar seems to support it (used by Pyright).
//
// I further tested how good editor support is for symlinked files and it is not good ;)
// * VS Code doesn't update the file content if a file gets changed through a symlink
// * PyCharm doesn't update diagnostics if a symlinked module is changed (same as red knot).
//
// That's why I think it's fine to not support this case for now.
let patched_baz_text = source_text(case.db(), patched_bar_baz_file);
let did_update_patched_baz = patched_baz_text.as_str() == "def baz(): print('Version 2')";
let bar_baz_text = source_text(case.db(), baz.file());
let did_update_bar_baz = bar_baz_text.as_str() == "def baz(): print('Version 2')";
assert!(
did_update_patched_baz || did_update_bar_baz,
"Expected one of the files to be updated but neither file was updated.\nOriginal: {patched_baz_text}\nSymlinked: {bar_baz_text}",
patched_baz_text = patched_baz_text.as_str(),
bar_baz_text = bar_baz_text.as_str()
);
Ok(())
}
/// A module search path is a symlink.
///
/// Setup:
/// ```text
/// - site-packages
/// | - bar/baz.py
///
/// - workspace
/// |-- .venv/lib/python3.12/site-packages -> /site-packages
/// |
/// |-- foo.py
/// ```
#[test]
fn symlinked_module_search_path() -> anyhow::Result<()> {
let mut case = setup_with_search_paths(
|root: &SystemPath, workspace: &SystemPath| {
// Set up the symlink target.
let site_packages = root.join("site-packages");
let bar = site_packages.join("bar");
std::fs::create_dir_all(bar.as_std_path())
.context("Failed to create bar directory")?;
let baz_original = bar.join("baz.py");
std::fs::write(baz_original.as_std_path(), "def baz(): ...")
.context("Failed to write baz.py")?;
// Symlink the site packages in the venv to the global site packages
let venv_site_packages = workspace.join(".venv/lib/python3.12/site-packages");
std::fs::create_dir_all(venv_site_packages.parent().unwrap())
.context("Failed to create .venv directory")?;
std::os::unix::fs::symlink(
site_packages.as_std_path(),
venv_site_packages.as_std_path(),
)
.context("Failed to create symlink to site-packages")?;
Ok(())
},
|_root, workspace| SearchPathSettings {
extra_paths: vec![],
workspace_root: workspace.to_path_buf(),
custom_typeshed: None,
site_packages: vec![workspace.join(".venv/lib/python3.12/site-packages")],
},
)?;
let baz = resolve_module(
case.db().upcast(),
ModuleName::new_static("bar.baz").unwrap(),
)
.expect("Expected bar.baz to exist in site-packages.");
let baz_site_packages =
case.workspace_path(".venv/lib/python3.12/site-packages/bar/baz.py");
let baz_original = case.root_path().join("site-packages/bar/baz.py");
let baz_original_file = case.system_file(&baz_original).unwrap();
assert_eq!(
source_text(case.db(), baz_original_file).as_str(),
"def baz(): ..."
);
assert_eq!(
source_text(case.db(), baz.file()).as_str(),
"def baz(): ..."
);
assert_eq!(
baz.file().path(case.db()).as_system_path(),
Some(&*baz_site_packages)
);
// Write to the symlink target.
update_file(&baz_original, "def baz(): print('Version 2')")
.context("Failed to update bar/baz.py")?;
let changes = case.stop_watch();
case.db_mut().apply_changes(changes);
assert_eq!(
source_text(case.db(), baz.file()).as_str(),
"def baz(): print('Version 2')"
);
// It would be nice if this is supported but the underlying file system watchers
// only emit a single event. For reference
// * VS Code doesn't update the file content if a file gets changed through a symlink
// * PyCharm doesn't update diagnostics if a symlinked module is changed (same as red knot).
// We could add support for it by keeping a reverse map from `real_path` to symlinked path but
// it doesn't seem worth doing considering that as prominent tools like PyCharm don't support it.
// Pyright does support it, thanks to chokidar.
assert_ne!(
source_text(case.db(), baz_original_file).as_str(),
"def baz(): print('Version 2')"
);
Ok(())
}
}

View File

@@ -1,29 +1,12 @@
use ruff_db::Upcast;
use crate::resolver::{
editable_install_resolution_paths, file_to_module, internal::ModuleNameIngredient,
module_resolution_settings, resolve_module_query,
};
use crate::typeshed::parse_typeshed_versions;
#[salsa::jar(db=Db)]
pub struct Jar(
ModuleNameIngredient<'_>,
module_resolution_settings,
editable_install_resolution_paths,
resolve_module_query,
file_to_module,
parse_typeshed_versions,
);
pub trait Db: salsa::DbWithJar<Jar> + ruff_db::Db + Upcast<dyn ruff_db::Db> {}
#[salsa::db]
pub trait Db: ruff_db::Db + Upcast<dyn ruff_db::Db> {}
#[cfg(test)]
pub(crate) mod tests {
use std::sync;
use salsa::DebugWithDb;
use ruff_db::files::Files;
use ruff_db::system::{DbWithTestSystem, TestSystem};
use ruff_db::vendored::VendoredFileSystem;
@@ -32,7 +15,7 @@ pub(crate) mod tests {
use super::*;
#[salsa::db(Jar, ruff_db::Jar)]
#[salsa::db]
pub(crate) struct TestDb {
storage: salsa::Storage<Self>,
system: TestSystem,
@@ -46,7 +29,7 @@ pub(crate) mod tests {
Self {
storage: salsa::Storage::default(),
system: TestSystem::default(),
vendored: vendored_typeshed_stubs().snapshot(),
vendored: vendored_typeshed_stubs().clone(),
events: sync::Arc::default(),
files: Files::default(),
}
@@ -81,6 +64,7 @@ pub(crate) mod tests {
}
}
#[salsa::db]
impl ruff_db::Db for TestDb {
fn vendored(&self) -> &VendoredFileSystem {
&self.vendored
@@ -95,6 +79,7 @@ pub(crate) mod tests {
}
}
#[salsa::db]
impl Db for TestDb {}
impl DbWithTestSystem for TestDb {
@@ -107,23 +92,14 @@ pub(crate) mod tests {
}
}
#[salsa::db]
impl salsa::Database for TestDb {
fn salsa_event(&self, event: salsa::Event) {
tracing::trace!("event: {:?}", event.debug(self));
let mut events = self.events.lock().unwrap();
events.push(event);
}
}
impl salsa::ParallelDatabase for TestDb {
fn snapshot(&self) -> salsa::Snapshot<Self> {
salsa::Snapshot::new(Self {
storage: self.storage.snapshot(),
system: self.system.snapshot(),
vendored: self.vendored.snapshot(),
files: self.files.snapshot(),
events: self.events.clone(),
})
self.attach(|_| {
tracing::trace!("event: {event:?}");
let mut events = self.events.lock().unwrap();
events.push(event);
});
}
}
}

View File

@@ -1,9 +1,9 @@
use std::iter::FusedIterator;
pub use db::{Db, Jar};
pub use db::Db;
pub use module::{Module, ModuleKind};
pub use module_name::ModuleName;
pub use resolver::{file_to_module, resolve_module};
pub use resolver::resolve_module;
use ruff_db::system::SystemPath;
pub use typeshed::{
vendored_typeshed_stubs, TypeshedVersionsParseError, TypeshedVersionsParseErrorKind,

View File

@@ -3,7 +3,6 @@ use std::sync::Arc;
use ruff_db::files::File;
use crate::db::Db;
use crate::module_name::ModuleName;
use crate::path::SearchPath;
@@ -62,17 +61,6 @@ impl std::fmt::Debug for Module {
}
}
impl salsa::DebugWithDb<dyn Db> for Module {
fn fmt(&self, f: &mut Formatter<'_>, db: &dyn Db) -> std::fmt::Result {
f.debug_struct("Module")
.field("name", &self.name())
.field("kind", &self.kind())
.field("file", &self.file().debug(db.upcast()))
.field("search_path", &self.search_path())
.finish()
}
}
#[derive(PartialEq, Eq)]
struct ModuleInner {
name: ModuleName,

View File

@@ -5,7 +5,7 @@ use std::sync::Arc;
use camino::{Utf8Path, Utf8PathBuf};
use ruff_db::files::{system_path_to_file, vendored_path_to_file, File, FilePath};
use ruff_db::files::{system_path_to_file, vendored_path_to_file, File, FileError};
use ruff_db::system::{System, SystemPath, SystemPathBuf};
use ruff_db::vendored::{VendoredPath, VendoredPathBuf};
@@ -68,16 +68,18 @@ impl ModulePath {
SearchPathInner::Extra(search_path)
| SearchPathInner::FirstParty(search_path)
| SearchPathInner::SitePackages(search_path)
| SearchPathInner::Editable(search_path) => resolver
.system()
.is_directory(&search_path.join(relative_path)),
| SearchPathInner::Editable(search_path) => {
system_path_to_file(resolver.db.upcast(), search_path.join(relative_path))
== Err(FileError::IsADirectory)
}
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
match query_stdlib_version(Some(stdlib_root), relative_path, resolver) {
TypeshedVersionsQueryResult::DoesNotExist => false,
TypeshedVersionsQueryResult::Exists
| TypeshedVersionsQueryResult::MaybeExists => resolver
.system()
.is_directory(&stdlib_root.join(relative_path)),
| TypeshedVersionsQueryResult::MaybeExists => {
system_path_to_file(resolver.db.upcast(), stdlib_root.join(relative_path))
== Err(FileError::IsADirectory)
}
}
}
SearchPathInner::StandardLibraryVendored(stdlib_root) => {
@@ -105,10 +107,9 @@ impl ModulePath {
| SearchPathInner::SitePackages(search_path)
| SearchPathInner::Editable(search_path) => {
let absolute_path = search_path.join(relative_path);
system_path_to_file(resolver.db.upcast(), absolute_path.join("__init__.py"))
.is_some()
system_path_to_file(resolver.db.upcast(), absolute_path.join("__init__.py")).is_ok()
|| system_path_to_file(resolver.db.upcast(), absolute_path.join("__init__.py"))
.is_some()
.is_ok()
}
SearchPathInner::StandardLibraryCustom(search_path) => {
match query_stdlib_version(Some(search_path), relative_path, resolver) {
@@ -118,7 +119,7 @@ impl ModulePath {
resolver.db.upcast(),
search_path.join(relative_path).join("__init__.pyi"),
)
.is_some(),
.is_ok(),
}
}
SearchPathInner::StandardLibraryVendored(search_path) => {
@@ -145,14 +146,14 @@ impl ModulePath {
| SearchPathInner::FirstParty(search_path)
| SearchPathInner::SitePackages(search_path)
| SearchPathInner::Editable(search_path) => {
system_path_to_file(db, search_path.join(relative_path))
system_path_to_file(db, search_path.join(relative_path)).ok()
}
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
match query_stdlib_version(Some(stdlib_root), relative_path, resolver) {
TypeshedVersionsQueryResult::DoesNotExist => None,
TypeshedVersionsQueryResult::Exists
| TypeshedVersionsQueryResult::MaybeExists => {
system_path_to_file(db, stdlib_root.join(relative_path))
system_path_to_file(db, stdlib_root.join(relative_path)).ok()
}
}
}
@@ -161,7 +162,7 @@ impl ModulePath {
TypeshedVersionsQueryResult::DoesNotExist => None,
TypeshedVersionsQueryResult::Exists
| TypeshedVersionsQueryResult::MaybeExists => {
vendored_path_to_file(db, stdlib_root.join(relative_path))
vendored_path_to_file(db, stdlib_root.join(relative_path)).ok()
}
}
}
@@ -301,11 +302,15 @@ pub(crate) enum SearchPathValidationError {
/// (This is only relevant for stdlib search paths.)
NoStdlibSubdirectory(SystemPathBuf),
/// The path provided by the user is a directory,
/// The typeshed path provided by the user is a directory,
/// but no `stdlib/VERSIONS` file exists.
/// (This is only relevant for stdlib search paths.)
NoVersionsFile(SystemPathBuf),
/// `stdlib/VERSIONS` is a directory.
/// (This is only relevant for stdlib search paths.)
VersionsIsADirectory(SystemPathBuf),
/// The path provided by the user is a directory,
/// and a `stdlib/VERSIONS` file exists, but it fails to parse.
/// (This is only relevant for stdlib search paths.)
@@ -319,7 +324,8 @@ impl fmt::Display for SearchPathValidationError {
Self::NoStdlibSubdirectory(path) => {
write!(f, "The directory at {path} has no `stdlib/` subdirectory")
}
Self::NoVersionsFile(path) => write!(f, "Expected a file at {path}/stldib/VERSIONS"),
Self::NoVersionsFile(path) => write!(f, "Expected a file at {path}/stdlib/VERSIONS"),
Self::VersionsIsADirectory(path) => write!(f, "{path}/stdlib/VERSIONS is a directory."),
Self::VersionsParseError(underlying_error) => underlying_error.fmt(f),
}
}
@@ -408,10 +414,13 @@ impl SearchPath {
typeshed.to_path_buf(),
));
}
let Some(typeshed_versions) = system_path_to_file(db.upcast(), stdlib.join("VERSIONS"))
else {
return Err(SearchPathValidationError::NoVersionsFile(typeshed));
};
let typeshed_versions =
system_path_to_file(db.upcast(), stdlib.join("VERSIONS")).map_err(|err| match err {
FileError::NotFound => SearchPathValidationError::NoVersionsFile(typeshed),
FileError::IsADirectory => {
SearchPathValidationError::VersionsIsADirectory(typeshed)
}
})?;
crate::typeshed::parse_typeshed_versions(db, typeshed_versions)
.as_ref()
.map_err(|validation_error| {
@@ -468,24 +477,21 @@ impl SearchPath {
)
}
/// Does this search path point to the `site-packages` directory?
#[must_use]
pub(crate) fn is_site_packages(&self) -> bool {
matches!(&*self.0, SearchPathInner::SitePackages(_))
fn is_valid_extension(&self, extension: &str) -> bool {
if self.is_standard_library() {
extension == "pyi"
} else {
matches!(extension, "pyi" | "py")
}
}
#[must_use]
pub(crate) fn relativize_path(&self, path: &FilePath) -> Option<ModulePath> {
let extension = path.extension();
if self.is_standard_library() {
if extension.is_some_and(|extension| extension != "pyi") {
return None;
}
} else {
if extension.is_some_and(|extension| !matches!(extension, "pyi" | "py")) {
return None;
}
pub(crate) fn relativize_system_path(&self, path: &SystemPath) -> Option<ModulePath> {
if path
.extension()
.is_some_and(|extension| !self.is_valid_extension(extension))
{
return None;
}
match &*self.0 {
@@ -493,16 +499,36 @@ impl SearchPath {
| SearchPathInner::FirstParty(search_path)
| SearchPathInner::StandardLibraryCustom(search_path)
| SearchPathInner::SitePackages(search_path)
| SearchPathInner::Editable(search_path) => path
.as_system_path()
.and_then(|absolute_path| absolute_path.strip_prefix(search_path).ok())
.map(|relative_path| ModulePath {
search_path: self.clone(),
relative_path: relative_path.as_utf8_path().to_path_buf(),
}),
| SearchPathInner::Editable(search_path) => {
path.strip_prefix(search_path)
.ok()
.map(|relative_path| ModulePath {
search_path: self.clone(),
relative_path: relative_path.as_utf8_path().to_path_buf(),
})
}
SearchPathInner::StandardLibraryVendored(_) => None,
}
}
#[must_use]
pub(crate) fn relativize_vendored_path(&self, path: &VendoredPath) -> Option<ModulePath> {
if path
.extension()
.is_some_and(|extension| !self.is_valid_extension(extension))
{
return None;
}
match &*self.0 {
SearchPathInner::Extra(_)
| SearchPathInner::FirstParty(_)
| SearchPathInner::StandardLibraryCustom(_)
| SearchPathInner::SitePackages(_)
| SearchPathInner::Editable(_) => None,
SearchPathInner::StandardLibraryVendored(search_path) => path
.as_vendored_path()
.and_then(|absolute_path| absolute_path.strip_prefix(search_path).ok())
.strip_prefix(search_path)
.ok()
.map(|relative_path| ModulePath {
search_path: self.clone(),
relative_path: relative_path.as_utf8_path().to_path_buf(),
@@ -792,14 +818,14 @@ mod tests {
let root = SearchPath::custom_stdlib(&db, stdlib.parent().unwrap().to_path_buf()).unwrap();
// Must have a `.pyi` extension or no extension:
let bad_absolute_path = FilePath::system("foo/stdlib/x.py");
assert_eq!(root.relativize_path(&bad_absolute_path), None);
let second_bad_absolute_path = FilePath::system("foo/stdlib/x.rs");
assert_eq!(root.relativize_path(&second_bad_absolute_path), None);
let bad_absolute_path = SystemPath::new("foo/stdlib/x.py");
assert_eq!(root.relativize_system_path(bad_absolute_path), None);
let second_bad_absolute_path = SystemPath::new("foo/stdlib/x.rs");
assert_eq!(root.relativize_system_path(second_bad_absolute_path), None);
// Must be a path that is a child of `root`:
let third_bad_absolute_path = FilePath::system("bar/stdlib/x.pyi");
assert_eq!(root.relativize_path(&third_bad_absolute_path), None);
let third_bad_absolute_path = SystemPath::new("bar/stdlib/x.pyi");
assert_eq!(root.relativize_system_path(third_bad_absolute_path), None);
}
#[test]
@@ -808,19 +834,21 @@ mod tests {
let root = SearchPath::extra(db.system(), src.clone()).unwrap();
// Must have a `.py` extension, a `.pyi` extension, or no extension:
let bad_absolute_path = FilePath::System(src.join("x.rs"));
assert_eq!(root.relativize_path(&bad_absolute_path), None);
let bad_absolute_path = src.join("x.rs");
assert_eq!(root.relativize_system_path(&bad_absolute_path), None);
// Must be a path that is a child of `root`:
let second_bad_absolute_path = FilePath::system("bar/src/x.pyi");
assert_eq!(root.relativize_path(&second_bad_absolute_path), None);
let second_bad_absolute_path = SystemPath::new("bar/src/x.pyi");
assert_eq!(root.relativize_system_path(second_bad_absolute_path), None);
}
#[test]
fn relativize_path() {
let TestCase { db, src, .. } = TestCaseBuilder::new().build();
let src_search_path = SearchPath::first_party(db.system(), src.clone()).unwrap();
let eggs_package = FilePath::System(src.join("eggs/__init__.pyi"));
let module_path = src_search_path.relativize_path(&eggs_package).unwrap();
let eggs_package = src.join("eggs/__init__.pyi");
let module_path = src_search_path
.relativize_system_path(&eggs_package)
.unwrap();
assert_eq!(
&module_path.relative_path,
Utf8Path::new("eggs/__init__.pyi")

View File

@@ -2,11 +2,11 @@ use std::borrow::Cow;
use std::iter::FusedIterator;
use once_cell::sync::Lazy;
use rustc_hash::{FxBuildHasher, FxHashSet};
use ruff_db::files::{File, FilePath};
use ruff_db::files::{File, FilePath, FileRootKind};
use ruff_db::program::{Program, SearchPathSettings, TargetVersion};
use ruff_db::system::{DirectoryEntry, System, SystemPath, SystemPathBuf};
use ruff_db::vendored::VendoredPath;
use rustc_hash::{FxBuildHasher, FxHashSet};
use crate::db::Db;
use crate::module::{Module, ModuleKind};
@@ -16,7 +16,7 @@ use crate::state::ResolverState;
/// Resolves a module name to a module.
pub fn resolve_module(db: &dyn Db, module_name: ModuleName) -> Option<Module> {
let interned_name = internal::ModuleNameIngredient::new(db, module_name);
let interned_name = ModuleNameIngredient::new(db, module_name);
resolve_module_query(db, interned_name)
}
@@ -28,7 +28,7 @@ pub fn resolve_module(db: &dyn Db, module_name: ModuleName) -> Option<Module> {
#[salsa::tracked]
pub(crate) fn resolve_module_query<'db>(
db: &'db dyn Db,
module_name: internal::ModuleNameIngredient<'db>,
module_name: ModuleNameIngredient<'db>,
) -> Option<Module> {
let name = module_name.name(db);
let _span = tracing::trace_span!("resolve_module", %name).entered();
@@ -57,15 +57,24 @@ pub(crate) fn path_to_module(db: &dyn Db, path: &FilePath) -> Option<Module> {
file_to_module(db, file)
}
#[derive(Debug, Clone, Copy)]
enum SystemOrVendoredPathRef<'a> {
System(&'a SystemPath),
Vendored(&'a VendoredPath),
}
/// Resolves the module for the file with the given id.
///
/// Returns `None` if the file is not a module locatable via any of the known search paths.
#[allow(unreachable_pub)]
#[salsa::tracked]
pub fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
let _span = tracing::trace_span!("file_to_module", ?file).entered();
let path = file.path(db.upcast());
let path = match file.path(db.upcast()) {
FilePath::System(system) => SystemOrVendoredPathRef::System(system),
FilePath::Vendored(vendored) => SystemOrVendoredPathRef::Vendored(vendored),
FilePath::SystemVirtual(_) => return None,
};
let settings = module_resolution_settings(db);
@@ -73,7 +82,11 @@ pub fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
let module_name = loop {
let candidate = search_paths.next()?;
if let Some(relative_path) = candidate.relativize_path(path) {
let relative_path = match path {
SystemOrVendoredPathRef::System(path) => candidate.relativize_system_path(path),
SystemOrVendoredPathRef::Vendored(path) => candidate.relativize_vendored_path(path),
};
if let Some(relative_path) = relative_path {
break relative_path.to_module_name()?;
}
};
@@ -125,25 +138,28 @@ fn try_resolve_module_resolution_settings(
}
let system = db.system();
let files = db.files();
let mut static_search_paths = vec![];
for path in extra_paths.iter().cloned() {
static_search_paths.push(SearchPath::extra(system, path)?);
for path in extra_paths {
files.try_add_root(db.upcast(), path, FileRootKind::LibrarySearchPath);
static_search_paths.push(SearchPath::extra(system, path.clone())?);
}
static_search_paths.push(SearchPath::first_party(system, workspace_root.clone())?);
static_search_paths.push(if let Some(custom_typeshed) = custom_typeshed.as_ref() {
files.try_add_root(
db.upcast(),
custom_typeshed,
FileRootKind::LibrarySearchPath,
);
SearchPath::custom_stdlib(db, custom_typeshed.clone())?
} else {
SearchPath::vendored_stdlib()
});
if let Some(site_packages) = site_packages {
static_search_paths.push(SearchPath::site_packages(system, site_packages.clone())?);
}
// TODO vendor typeshed's third-party stubs as well as the stdlib and fallback to them as a final step
let target_version = program.target_version(db.upcast());
@@ -169,6 +185,7 @@ fn try_resolve_module_resolution_settings(
Ok(ModuleResolutionSettings {
target_version,
static_search_paths,
site_packages_paths: site_packages.to_owned(),
})
}
@@ -178,35 +195,55 @@ pub(crate) fn module_resolution_settings(db: &dyn Db) -> ModuleResolutionSetting
try_resolve_module_resolution_settings(db).unwrap()
}
/// Collect all dynamic search paths:
/// search paths listed in `.pth` files in the `site-packages` directory
/// due to editable installations of third-party packages.
/// 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
/// due to editable installations of third-party packages.
///
/// The editable-install search paths for the first `site-packages` directory
/// should come between the two `site-packages` directories when it comes to
/// module-resolution priority.
#[salsa::tracked(return_ref)]
pub(crate) fn editable_install_resolution_paths(db: &dyn Db) -> Vec<SearchPath> {
// This query needs to be re-executed each time a `.pth` file
// is added, modified or removed from the `site-packages` directory.
// However, we don't use Salsa queries to read the source text of `.pth` files;
// we use the APIs on the `System` trait directly. As such, for now we simply ask
// Salsa to recompute this query on each new revision.
//
// TODO: add some kind of watcher for the `site-packages` directory that looks
// for `site-packages/*.pth` files being added/modified/removed; get rid of this.
// When doing so, also make the test
// `deleting_pth_file_on_which_module_resolution_depends_invalidates_cache()`
// more principled!
db.report_untracked_read();
pub(crate) fn dynamic_resolution_paths(db: &dyn Db) -> Vec<SearchPath> {
let ModuleResolutionSettings {
target_version: _,
static_search_paths,
site_packages_paths,
} = module_resolution_settings(db);
let static_search_paths = &module_resolution_settings(db).static_search_paths;
let site_packages = static_search_paths
let mut dynamic_paths = Vec::new();
if site_packages_paths.is_empty() {
return dynamic_paths;
}
let mut existing_paths: FxHashSet<_> = static_search_paths
.iter()
.find(|path| path.is_site_packages());
.filter_map(|path| path.as_system_path())
.map(Cow::Borrowed)
.collect();
let mut dynamic_paths = Vec::default();
let files = db.files();
let system = db.system();
if let Some(site_packages) = site_packages {
let site_packages = site_packages
.as_system_path()
.expect("Expected site-packages never to be a VendoredPath!");
for site_packages_dir in site_packages_paths {
if !existing_paths.insert(Cow::Borrowed(site_packages_dir)) {
continue;
}
let site_packages_root = files.try_add_root(
db.upcast(),
site_packages_dir,
FileRootKind::LibrarySearchPath,
);
// This query needs to be re-executed each time a `.pth` file
// is added, modified or removed from the `site-packages` directory.
// However, we don't use Salsa queries to read the source text of `.pth` files;
// we use the APIs on the `System` trait directly. As such, add a dependency on the
// site-package directory's revision.
site_packages_root.revision(db.upcast());
dynamic_paths
.push(SearchPath::site_packages(system, site_packages_dir.to_owned()).unwrap());
// As well as modules installed directly into `site-packages`,
// the directory may also contain `.pth` files.
@@ -214,8 +251,8 @@ pub(crate) fn editable_install_resolution_paths(db: &dyn Db) -> Vec<SearchPath>
// containing a (relative or absolute) path.
// Each of these paths may point to an editable install of a package,
// so should be considered an additional search path.
let Ok(pth_file_iterator) = PthFileIterator::new(db, site_packages) else {
return dynamic_paths;
let Ok(pth_file_iterator) = PthFileIterator::new(db, site_packages_dir) else {
continue;
};
// The Python documentation specifies that `.pth` files in `site-packages`
@@ -224,14 +261,6 @@ pub(crate) fn editable_install_resolution_paths(db: &dyn Db) -> Vec<SearchPath>
let mut all_pth_files: Vec<PthFile> = pth_file_iterator.collect();
all_pth_files.sort_by(|a, b| a.path.cmp(&b.path));
let mut existing_paths: FxHashSet<_> = static_search_paths
.iter()
.filter_map(|path| path.as_system_path())
.map(Cow::Borrowed)
.collect();
dynamic_paths.reserve(all_pth_files.len());
for pth_file in &all_pth_files {
for installation in pth_file.editable_installations() {
if existing_paths.insert(Cow::Owned(
@@ -271,7 +300,7 @@ impl<'db> Iterator for SearchPathIterator<'db> {
static_paths.next().or_else(|| {
dynamic_paths
.get_or_insert_with(|| editable_install_resolution_paths(*db).iter())
.get_or_insert_with(|| dynamic_resolution_paths(*db).iter())
.next()
})
}
@@ -381,12 +410,18 @@ impl<'db> Iterator for PthFileIterator<'db> {
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ModuleResolutionSettings {
target_version: TargetVersion,
/// Search paths that have been statically determined purely from reading Ruff's configuration settings.
/// These shouldn't ever change unless the config settings themselves change.
///
/// Note that `site-packages` *is included* as a search path in this sequence,
/// but it is also stored separately so that we're able to find editable installs later.
static_search_paths: Vec<SearchPath>,
/// site-packages paths are not included in the above field:
/// if there are multiple site-packages paths, editable installations can appear
/// *between* the site-packages paths on `sys.path` at runtime.
/// That means we can't know where a second or third `site-packages` path should sit
/// in terms of module-resolution priority until we've discovered the editable installs
/// for the first `site-packages` path
site_packages_paths: Vec<SystemPathBuf>,
}
impl ModuleResolutionSettings {
@@ -403,22 +438,13 @@ impl ModuleResolutionSettings {
}
}
// The singleton methods generated by salsa are all `pub` instead of `pub(crate)` which triggers
// `unreachable_pub`. Work around this by creating a module and allow `unreachable_pub` for it.
// Salsa also generates uses to `_db` variables for `interned` which triggers `clippy::used_underscore_binding`. Suppress that too
// TODO(micha): Contribute a fix for this upstream where the singleton methods have the same visibility as the struct.
#[allow(unreachable_pub, clippy::used_underscore_binding)]
pub(crate) mod internal {
use crate::module_name::ModuleName;
/// A thin wrapper around `ModuleName` to make it a Salsa ingredient.
///
/// This is needed because Salsa requires that all query arguments are salsa ingredients.
#[salsa::interned]
pub(crate) struct ModuleNameIngredient<'db> {
#[return_ref]
pub(super) name: ModuleName,
}
/// A thin wrapper around `ModuleName` to make it a Salsa ingredient.
///
/// This is needed because Salsa requires that all query arguments are salsa ingredients.
#[salsa::interned]
struct ModuleNameIngredient<'db> {
#[return_ref]
pub(super) name: ModuleName,
}
/// Modules that are builtin to the Python interpreter itself.
@@ -478,6 +504,7 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, File, Mod
if is_builtin_module && !search_path.is_standard_library() {
continue;
}
let mut components = name.components();
let module_name = components.next_back()?;
@@ -612,10 +639,11 @@ impl PackageKind {
#[cfg(test)]
mod tests {
use internal::ModuleNameIngredient;
use ruff_db::files::{system_path_to_file, File, FilePath};
use ruff_db::system::{DbWithTestSystem, OsSystem, SystemPath};
use ruff_db::testing::assert_function_query_was_not_run;
use ruff_db::system::DbWithTestSystem;
use ruff_db::testing::{
assert_const_function_query_was_not_run, assert_function_query_was_not_run,
};
use ruff_db::Db;
use crate::db::tests::TestDb;
@@ -1140,7 +1168,9 @@ mod tests {
#[test]
#[cfg(target_family = "unix")]
fn symlink() -> anyhow::Result<()> {
use crate::db::tests::TestDb;
use ruff_db::program::Program;
use ruff_db::system::{OsSystem, SystemPath};
let mut db = TestDb::new();
@@ -1167,7 +1197,7 @@ mod tests {
extra_paths: vec![],
workspace_root: src.clone(),
custom_typeshed: Some(custom_typeshed.clone()),
site_packages: Some(site_packages.clone()),
site_packages: vec![site_packages],
};
Program::new(&db, TargetVersion::Py38, search_paths);
@@ -1270,6 +1300,7 @@ mod tests {
db.memory_file_system()
.remove_directory(foo_init_path.parent().unwrap())?;
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");
assert_eq!(&src.join("foo.py"), foo_module.file().path(&db));
@@ -1300,7 +1331,7 @@ mod tests {
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(
Some(functools_module.file()),
Ok(functools_module.file()),
system_path_to_file(&db, &stdlib_functools_path)
);
@@ -1312,15 +1343,15 @@ mod tests {
.unwrap();
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
let events = db.take_salsa_events();
assert_function_query_was_not_run::<resolve_module_query, _, _>(
assert_function_query_was_not_run(
&db,
|res| &res.function,
&ModuleNameIngredient::new(&db, functools_module_name.clone()),
resolve_module_query,
ModuleNameIngredient::new(&db, functools_module_name.clone()),
&events,
);
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(
Some(functools_module.file()),
Ok(functools_module.file()),
system_path_to_file(&db, &stdlib_functools_path)
);
}
@@ -1346,7 +1377,7 @@ mod tests {
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(
Some(functools_module.file()),
Ok(functools_module.file()),
system_path_to_file(&db, stdlib.join("functools.pyi"))
);
@@ -1357,7 +1388,7 @@ mod tests {
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
assert_eq!(functools_module.search_path(), &src);
assert_eq!(
Some(functools_module.file()),
Ok(functools_module.file()),
system_path_to_file(&db, &src_functools_path)
);
}
@@ -1388,7 +1419,7 @@ mod tests {
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
assert_eq!(functools_module.search_path(), &src);
assert_eq!(
Some(functools_module.file()),
Ok(functools_module.file()),
system_path_to_file(&db, &src_functools_path)
);
@@ -1401,7 +1432,7 @@ mod tests {
let functools_module = resolve_module(&db, functools_module_name.clone()).unwrap();
assert_eq!(functools_module.search_path(), &stdlib);
assert_eq!(
Some(functools_module.file()),
Ok(functools_module.file()),
system_path_to_file(&db, stdlib.join("functools.pyi"))
);
}
@@ -1564,12 +1595,7 @@ not_a_directory
&FilePath::system("/y/src/bar.py")
);
let events = db.take_salsa_events();
assert_function_query_was_not_run::<editable_install_resolution_paths, _, _>(
&db,
|res| &res.function,
&(),
&events,
);
assert_const_function_query_was_not_run(&db, dynamic_resolution_paths, &events);
}
#[test]
@@ -1598,18 +1624,7 @@ not_a_directory
.remove_file(site_packages.join("_foo.pth"))
.unwrap();
// Why are we touching a random file in the path that's been editably installed,
// rather than the `.pth` file, when the `.pth` file is the one that has been deleted?
// It's because the `.pth` file isn't directly tracked as a dependency by Salsa
// currently (we don't use `system_path_to_file()` to get the file, and we don't use
// `source_text()` to read the source of the file). Instead of using these APIs which
// would automatically add the existence and contents of the file as a Salsa-tracked
// dependency, we use `.report_untracked_read()` to force Salsa to re-parse all
// `.pth` files on each new "revision". Making a random modification to a tracked
// Salsa file forces a new revision.
//
// TODO: get rid of the `.report_untracked_read()` call...
File::sync_path(&mut db, SystemPath::new("/x/src/foo.py"));
File::sync_path(&mut db, &site_packages.join("_foo.pth"));
assert_eq!(resolve_module(&db, foo_module_name.clone()), None);
}
@@ -1658,4 +1673,53 @@ not_a_directory
assert!(!search_paths
.contains(&&SearchPath::editable(db.system(), SystemPathBuf::from("/src")).unwrap()));
}
#[test]
fn multiple_site_packages_with_editables() {
let mut db = TestDb::new();
let venv_site_packages = SystemPathBuf::from("/venv-site-packages");
let site_packages_pth = venv_site_packages.join("foo.pth");
let system_site_packages = SystemPathBuf::from("/system-site-packages");
let editable_install_location = SystemPathBuf::from("/x/y/a.py");
let system_site_packages_location = system_site_packages.join("a.py");
db.memory_file_system()
.create_directory_all("/src")
.unwrap();
db.write_files([
(&site_packages_pth, "/x/y"),
(&editable_install_location, ""),
(&system_site_packages_location, ""),
])
.unwrap();
Program::new(
&db,
TargetVersion::default(),
SearchPathSettings {
extra_paths: vec![],
workspace_root: SystemPathBuf::from("/src"),
custom_typeshed: None,
site_packages: vec![venv_site_packages, system_site_packages],
},
);
// 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();
assert_eq!(a_module.file().path(&db), &editable_install_location);
db.memory_file_system()
.remove_file(&site_packages_pth)
.unwrap();
File::sync_path(&mut db, &site_packages_pth);
// ...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();
assert_eq!(a_module.file().path(&db), &system_site_packages_location);
}
}

View File

@@ -1,5 +1,4 @@
use ruff_db::program::TargetVersion;
use ruff_db::system::System;
use ruff_db::vendored::VendoredFileSystem;
use crate::db::Db;
@@ -20,10 +19,6 @@ impl<'db> ResolverState<'db> {
}
}
pub(crate) fn system(&self) -> &dyn System {
self.db.system()
}
pub(crate) fn vendored(&self) -> &VendoredFileSystem {
self.db.vendored()
}

View File

@@ -12,6 +12,9 @@ pub(crate) struct TestCase<T> {
pub(crate) db: TestDb,
pub(crate) src: SystemPathBuf,
pub(crate) stdlib: T,
// Most test cases only ever need a single `site-packages` directory,
// so this is a single directory instead of a `Vec` of directories,
// like it is in `ruff_db::Program`.
pub(crate) site_packages: SystemPathBuf,
pub(crate) target_version: TargetVersion,
}
@@ -223,7 +226,7 @@ impl TestCaseBuilder<MockedTypeshed> {
extra_paths: vec![],
workspace_root: src.clone(),
custom_typeshed: Some(typeshed.clone()),
site_packages: Some(site_packages.clone()),
site_packages: vec![site_packages.clone()],
},
);
@@ -276,7 +279,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
extra_paths: vec![],
workspace_root: src.clone(),
custom_typeshed: None,
site_packages: Some(site_packages.clone()),
site_packages: vec![site_packages.clone()],
},
);

View File

@@ -52,7 +52,7 @@ impl<'db> LazyTypeshedVersions<'db> {
} else {
return &VENDORED_VERSIONS;
};
let Some(versions_file) = system_path_to_file(db.upcast(), &versions_path) else {
let Ok(versions_file) = system_path_to_file(db.upcast(), &versions_path) else {
todo!(
"Still need to figure out how to handle VERSIONS files being deleted \
from custom typeshed directories! Expected a file to exist at {versions_path}"

View File

@@ -1 +1 @@
f863db6bc5242348ceaa6a3bca4e59aa9e62faaa
4ef2d66663fc080fefa379e6ae5fc45d4f8b54eb

View File

@@ -1,18 +1,18 @@
import sys
from _typeshed import SupportsWrite
from collections.abc import Iterable, Iterator
from typing import Any, Final, Literal
from typing import Any, Final
from typing_extensions import TypeAlias
__version__: Final[str]
QUOTE_ALL: Literal[1]
QUOTE_MINIMAL: Literal[0]
QUOTE_NONE: Literal[3]
QUOTE_NONNUMERIC: Literal[2]
QUOTE_ALL: Final = 1
QUOTE_MINIMAL: Final = 0
QUOTE_NONE: Final = 3
QUOTE_NONNUMERIC: Final = 2
if sys.version_info >= (3, 12):
QUOTE_STRINGS: Literal[4]
QUOTE_NOTNULL: Literal[5]
QUOTE_STRINGS: Final = 4
QUOTE_NOTNULL: Final = 5
# Ideally this would be `QUOTE_ALL | QUOTE_MINIMAL | QUOTE_NONE | QUOTE_NONNUMERIC`
# However, using literals in situations like these can cause false-positives (see #7258)

View File

@@ -71,7 +71,7 @@ class _CData(metaclass=_CDataMeta):
@classmethod
def from_address(cls, address: int) -> Self: ...
@classmethod
def from_param(cls, obj: Any) -> Self | _CArgObject: ...
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
@classmethod
def in_dll(cls, library: CDLL, name: str) -> Self: ...
def __buffer__(self, flags: int, /) -> memoryview: ...

View File

@@ -368,11 +368,7 @@ def tparm(
) -> bytes: ...
def typeahead(fd: int, /) -> None: ...
def unctrl(ch: _ChType, /) -> bytes: ...
if sys.version_info < (3, 12) or sys.platform != "darwin":
# The support for macos was dropped in 3.12
def unget_wch(ch: int | str, /) -> None: ...
def unget_wch(ch: int | str, /) -> None: ...
def ungetch(ch: _ChType, /) -> None: ...
def ungetmouse(id: int, x: int, y: int, z: int, bstate: int, /) -> None: ...
def update_lines_cols() -> None: ...
@@ -447,13 +443,10 @@ class _CursesWindow:
def getch(self) -> int: ...
@overload
def getch(self, y: int, x: int) -> int: ...
if sys.version_info < (3, 12) or sys.platform != "darwin":
# The support for macos was dropped in 3.12
@overload
def get_wch(self) -> int | str: ...
@overload
def get_wch(self, y: int, x: int) -> int | str: ...
@overload
def get_wch(self) -> int | str: ...
@overload
def get_wch(self, y: int, x: int) -> int | str: ...
@overload
def getkey(self) -> str: ...
@overload

View File

@@ -17,20 +17,20 @@ class DecimalTuple(NamedTuple):
digits: tuple[int, ...]
exponent: int | Literal["n", "N", "F"]
ROUND_DOWN: str
ROUND_HALF_UP: str
ROUND_HALF_EVEN: str
ROUND_CEILING: str
ROUND_FLOOR: str
ROUND_UP: str
ROUND_HALF_DOWN: str
ROUND_05UP: str
HAVE_CONTEXTVAR: bool
HAVE_THREADS: bool
MAX_EMAX: int
MAX_PREC: int
MIN_EMIN: int
MIN_ETINY: int
ROUND_DOWN: Final[str]
ROUND_HALF_UP: Final[str]
ROUND_HALF_EVEN: Final[str]
ROUND_CEILING: Final[str]
ROUND_FLOOR: Final[str]
ROUND_UP: Final[str]
ROUND_HALF_DOWN: Final[str]
ROUND_05UP: Final[str]
HAVE_CONTEXTVAR: Final[bool]
HAVE_THREADS: Final[bool]
MAX_EMAX: Final[int]
MAX_PREC: Final[int]
MIN_EMIN: Final[int]
MIN_ETINY: Final[int]
class DecimalException(ArithmeticError): ...
class Clamped(DecimalException): ...

View File

@@ -1,5 +1,5 @@
from _typeshed import structseq
from typing import Final, Literal, SupportsIndex, final
from typing import Any, Final, Literal, SupportsIndex, final
from typing_extensions import Buffer, Self
class ChannelError(RuntimeError): ...
@@ -72,13 +72,15 @@ class ChannelInfo(structseq[int], tuple[bool, bool, bool, int, int, int, int, in
@property
def send_released(self) -> bool: ...
def create() -> ChannelID: ...
def create(unboundop: Literal[1, 2, 3]) -> ChannelID: ...
def destroy(cid: SupportsIndex) -> None: ...
def list_all() -> list[ChannelID]: ...
def list_interpreters(cid: SupportsIndex, *, send: bool) -> list[int]: ...
def send(cid: SupportsIndex, obj: object, *, blocking: bool = True, timeout: float | None = None) -> None: ...
def send_buffer(cid: SupportsIndex, obj: Buffer, *, blocking: bool = True, timeout: float | None = None) -> None: ...
def recv(cid: SupportsIndex, default: object = ...) -> object: ...
def recv(cid: SupportsIndex, default: object = ...) -> tuple[Any, Literal[1, 2, 3]]: ...
def close(cid: SupportsIndex, *, send: bool = False, recv: bool = False) -> None: ...
def get_count(cid: SupportsIndex) -> int: ...
def get_info(cid: SupportsIndex) -> ChannelInfo: ...
def get_channel_defaults(cid: SupportsIndex) -> Literal[1, 2, 3]: ...
def release(cid: SupportsIndex, *, send: bool = False, recv: bool = False, force: bool = False) -> None: ...

View File

@@ -1,5 +1,5 @@
from collections.abc import Iterable, Sequence
from typing import TypeVar
from typing import Final, TypeVar
_T = TypeVar("_T")
_K = TypeVar("_K")
@@ -7,15 +7,15 @@ _V = TypeVar("_V")
__all__ = ["compiler_fixup", "customize_config_vars", "customize_compiler", "get_platform_osx"]
_UNIVERSAL_CONFIG_VARS: tuple[str, ...] # undocumented
_COMPILER_CONFIG_VARS: tuple[str, ...] # undocumented
_INITPRE: str # undocumented
_UNIVERSAL_CONFIG_VARS: Final[tuple[str, ...]] # undocumented
_COMPILER_CONFIG_VARS: Final[tuple[str, ...]] # undocumented
_INITPRE: Final[str] # undocumented
def _find_executable(executable: str, path: str | None = None) -> str | None: ... # undocumented
def _read_output(commandstring: str, capture_stderr: bool = False) -> str | None: ... # undocumented
def _find_build_tool(toolname: str) -> str: ... # undocumented
_SYSTEM_VERSION: str | None # undocumented
_SYSTEM_VERSION: Final[str | None] # undocumented
def _get_system_version() -> str: ... # undocumented
def _remove_original_values(_config_vars: dict[str, str]) -> None: ... # undocumented

View File

@@ -1,30 +1,30 @@
import sys
from typing import Literal
from typing import Final
SF_APPEND: Literal[0x00040000]
SF_ARCHIVED: Literal[0x00010000]
SF_IMMUTABLE: Literal[0x00020000]
SF_NOUNLINK: Literal[0x00100000]
SF_SNAPSHOT: Literal[0x00200000]
SF_APPEND: Final = 0x00040000
SF_ARCHIVED: Final = 0x00010000
SF_IMMUTABLE: Final = 0x00020000
SF_NOUNLINK: Final = 0x00100000
SF_SNAPSHOT: Final = 0x00200000
ST_MODE: Literal[0]
ST_INO: Literal[1]
ST_DEV: Literal[2]
ST_NLINK: Literal[3]
ST_UID: Literal[4]
ST_GID: Literal[5]
ST_SIZE: Literal[6]
ST_ATIME: Literal[7]
ST_MTIME: Literal[8]
ST_CTIME: Literal[9]
ST_MODE: Final = 0
ST_INO: Final = 1
ST_DEV: Final = 2
ST_NLINK: Final = 3
ST_UID: Final = 4
ST_GID: Final = 5
ST_SIZE: Final = 6
ST_ATIME: Final = 7
ST_MTIME: Final = 8
ST_CTIME: Final = 9
S_IFIFO: Literal[0o010000]
S_IFLNK: Literal[0o120000]
S_IFREG: Literal[0o100000]
S_IFSOCK: Literal[0o140000]
S_IFBLK: Literal[0o060000]
S_IFCHR: Literal[0o020000]
S_IFDIR: Literal[0o040000]
S_IFIFO: Final = 0o010000
S_IFLNK: Final = 0o120000
S_IFREG: Final = 0o100000
S_IFSOCK: Final = 0o140000
S_IFBLK: Final = 0o060000
S_IFCHR: Final = 0o020000
S_IFDIR: Final = 0o040000
# These are 0 on systems that don't support the specific kind of file.
# Example: Linux doesn't support door files, so S_IFDOOR is 0 on linux.
@@ -32,37 +32,37 @@ S_IFDOOR: int
S_IFPORT: int
S_IFWHT: int
S_ISUID: Literal[0o4000]
S_ISGID: Literal[0o2000]
S_ISVTX: Literal[0o1000]
S_ISUID: Final = 0o4000
S_ISGID: Final = 0o2000
S_ISVTX: Final = 0o1000
S_IRWXU: Literal[0o0700]
S_IRUSR: Literal[0o0400]
S_IWUSR: Literal[0o0200]
S_IXUSR: Literal[0o0100]
S_IRWXU: Final = 0o0700
S_IRUSR: Final = 0o0400
S_IWUSR: Final = 0o0200
S_IXUSR: Final = 0o0100
S_IRWXG: Literal[0o0070]
S_IRGRP: Literal[0o0040]
S_IWGRP: Literal[0o0020]
S_IXGRP: Literal[0o0010]
S_IRWXG: Final = 0o0070
S_IRGRP: Final = 0o0040
S_IWGRP: Final = 0o0020
S_IXGRP: Final = 0o0010
S_IRWXO: Literal[0o0007]
S_IROTH: Literal[0o0004]
S_IWOTH: Literal[0o0002]
S_IXOTH: Literal[0o0001]
S_IRWXO: Final = 0o0007
S_IROTH: Final = 0o0004
S_IWOTH: Final = 0o0002
S_IXOTH: Final = 0o0001
S_ENFMT: Literal[0o2000]
S_IREAD: Literal[0o0400]
S_IWRITE: Literal[0o0200]
S_IEXEC: Literal[0o0100]
S_ENFMT: Final = 0o2000
S_IREAD: Final = 0o0400
S_IWRITE: Final = 0o0200
S_IEXEC: Final = 0o0100
UF_APPEND: Literal[0x00000004]
UF_COMPRESSED: Literal[0x00000020] # OS X 10.6+ only
UF_HIDDEN: Literal[0x00008000] # OX X 10.5+ only
UF_IMMUTABLE: Literal[0x00000002]
UF_NODUMP: Literal[0x00000001]
UF_NOUNLINK: Literal[0x00000010]
UF_OPAQUE: Literal[0x00000008]
UF_APPEND: Final = 0x00000004
UF_COMPRESSED: Final = 0x00000020 # OS X 10.6+ only
UF_HIDDEN: Final = 0x00008000 # OX X 10.5+ only
UF_IMMUTABLE: Final = 0x00000002
UF_NODUMP: Final = 0x00000001
UF_NOUNLINK: Final = 0x00000010
UF_OPAQUE: Final = 0x00000008
def S_IMODE(mode: int, /) -> int: ...
def S_IFMT(mode: int, /) -> int: ...
@@ -84,34 +84,36 @@ if sys.platform == "win32":
IO_REPARSE_TAG_APPEXECLINK: int
if sys.platform == "win32":
FILE_ATTRIBUTE_ARCHIVE: Literal[32]
FILE_ATTRIBUTE_COMPRESSED: Literal[2048]
FILE_ATTRIBUTE_DEVICE: Literal[64]
FILE_ATTRIBUTE_DIRECTORY: Literal[16]
FILE_ATTRIBUTE_ENCRYPTED: Literal[16384]
FILE_ATTRIBUTE_HIDDEN: Literal[2]
FILE_ATTRIBUTE_INTEGRITY_STREAM: Literal[32768]
FILE_ATTRIBUTE_NORMAL: Literal[128]
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Literal[8192]
FILE_ATTRIBUTE_NO_SCRUB_DATA: Literal[131072]
FILE_ATTRIBUTE_OFFLINE: Literal[4096]
FILE_ATTRIBUTE_READONLY: Literal[1]
FILE_ATTRIBUTE_REPARSE_POINT: Literal[1024]
FILE_ATTRIBUTE_SPARSE_FILE: Literal[512]
FILE_ATTRIBUTE_SYSTEM: Literal[4]
FILE_ATTRIBUTE_TEMPORARY: Literal[256]
FILE_ATTRIBUTE_VIRTUAL: Literal[65536]
FILE_ATTRIBUTE_ARCHIVE: Final = 32
FILE_ATTRIBUTE_COMPRESSED: Final = 2048
FILE_ATTRIBUTE_DEVICE: Final = 64
FILE_ATTRIBUTE_DIRECTORY: Final = 16
FILE_ATTRIBUTE_ENCRYPTED: Final = 16384
FILE_ATTRIBUTE_HIDDEN: Final = 2
FILE_ATTRIBUTE_INTEGRITY_STREAM: Final = 32768
FILE_ATTRIBUTE_NORMAL: Final = 128
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Final = 8192
FILE_ATTRIBUTE_NO_SCRUB_DATA: Final = 131072
FILE_ATTRIBUTE_OFFLINE: Final = 4096
FILE_ATTRIBUTE_READONLY: Final = 1
FILE_ATTRIBUTE_REPARSE_POINT: Final = 1024
FILE_ATTRIBUTE_SPARSE_FILE: Final = 512
FILE_ATTRIBUTE_SYSTEM: Final = 4
FILE_ATTRIBUTE_TEMPORARY: Final = 256
FILE_ATTRIBUTE_VIRTUAL: Final = 65536
if sys.version_info >= (3, 13):
SF_SETTABLE: Literal[0x3FFF0000]
# Varies by platform.
SF_SETTABLE: Final[int]
# https://github.com/python/cpython/issues/114081#issuecomment-2119017790
# SF_RESTRICTED: Literal[0x00080000]
SF_FIRMLINK: Literal[0x00800000]
SF_DATALESS: Literal[0x40000000]
SF_FIRMLINK: Final = 0x00800000
SF_DATALESS: Final = 0x40000000
SF_SUPPORTED: Literal[0x9F0000]
SF_SYNTHETIC: Literal[0xC0000000]
if sys.platform == "darwin":
SF_SUPPORTED: Final = 0x9F0000
SF_SYNTHETIC: Final = 0xC0000000
UF_TRACKED: Literal[0x00000040]
UF_DATAVAULT: Literal[0x00000080]
UF_SETTABLE: Literal[0x0000FFFF]
UF_TRACKED: Final = 0x00000040
UF_DATAVAULT: Final = 0x00000080
UF_SETTABLE: Final = 0x0000FFFF

View File

@@ -1,6 +1,6 @@
import sys
from collections.abc import Callable
from typing import Any, ClassVar, Literal, final
from typing import Any, ClassVar, Final, final
from typing_extensions import TypeAlias
# _tkinter is meant to be only used internally by tkinter, but some tkinter
@@ -95,16 +95,16 @@ class TkappType:
def settrace(self, func: _TkinterTraceFunc | None, /) -> None: ...
# These should be kept in sync with tkinter.tix constants, except ALL_EVENTS which doesn't match TCL_ALL_EVENTS
ALL_EVENTS: Literal[-3]
FILE_EVENTS: Literal[8]
IDLE_EVENTS: Literal[32]
TIMER_EVENTS: Literal[16]
WINDOW_EVENTS: Literal[4]
ALL_EVENTS: Final = -3
FILE_EVENTS: Final = 8
IDLE_EVENTS: Final = 32
TIMER_EVENTS: Final = 16
WINDOW_EVENTS: Final = 4
DONT_WAIT: Literal[2]
EXCEPTION: Literal[8]
READABLE: Literal[2]
WRITABLE: Literal[4]
DONT_WAIT: Final = 2
EXCEPTION: Final = 8
READABLE: Final = 2
WRITABLE: Final = 4
TCL_VERSION: str
TK_VERSION: str

View File

@@ -1,117 +1,117 @@
import sys
from _typeshed import ReadableBuffer
from collections.abc import Sequence
from typing import Any, Literal, NoReturn, final, overload
from typing import Any, Final, Literal, NoReturn, final, overload
if sys.platform == "win32":
ABOVE_NORMAL_PRIORITY_CLASS: Literal[0x8000]
BELOW_NORMAL_PRIORITY_CLASS: Literal[0x4000]
ABOVE_NORMAL_PRIORITY_CLASS: Final = 0x8000
BELOW_NORMAL_PRIORITY_CLASS: Final = 0x4000
CREATE_BREAKAWAY_FROM_JOB: Literal[0x1000000]
CREATE_DEFAULT_ERROR_MODE: Literal[0x4000000]
CREATE_NO_WINDOW: Literal[0x8000000]
CREATE_NEW_CONSOLE: Literal[0x10]
CREATE_NEW_PROCESS_GROUP: Literal[0x200]
CREATE_BREAKAWAY_FROM_JOB: Final = 0x1000000
CREATE_DEFAULT_ERROR_MODE: Final = 0x4000000
CREATE_NO_WINDOW: Final = 0x8000000
CREATE_NEW_CONSOLE: Final = 0x10
CREATE_NEW_PROCESS_GROUP: Final = 0x200
DETACHED_PROCESS: Literal[8]
DUPLICATE_CLOSE_SOURCE: Literal[1]
DUPLICATE_SAME_ACCESS: Literal[2]
DETACHED_PROCESS: Final = 8
DUPLICATE_CLOSE_SOURCE: Final = 1
DUPLICATE_SAME_ACCESS: Final = 2
ERROR_ALREADY_EXISTS: Literal[183]
ERROR_BROKEN_PIPE: Literal[109]
ERROR_IO_PENDING: Literal[997]
ERROR_MORE_DATA: Literal[234]
ERROR_NETNAME_DELETED: Literal[64]
ERROR_NO_DATA: Literal[232]
ERROR_NO_SYSTEM_RESOURCES: Literal[1450]
ERROR_OPERATION_ABORTED: Literal[995]
ERROR_PIPE_BUSY: Literal[231]
ERROR_PIPE_CONNECTED: Literal[535]
ERROR_SEM_TIMEOUT: Literal[121]
ERROR_ALREADY_EXISTS: Final = 183
ERROR_BROKEN_PIPE: Final = 109
ERROR_IO_PENDING: Final = 997
ERROR_MORE_DATA: Final = 234
ERROR_NETNAME_DELETED: Final = 64
ERROR_NO_DATA: Final = 232
ERROR_NO_SYSTEM_RESOURCES: Final = 1450
ERROR_OPERATION_ABORTED: Final = 995
ERROR_PIPE_BUSY: Final = 231
ERROR_PIPE_CONNECTED: Final = 535
ERROR_SEM_TIMEOUT: Final = 121
FILE_FLAG_FIRST_PIPE_INSTANCE: Literal[0x80000]
FILE_FLAG_OVERLAPPED: Literal[0x40000000]
FILE_FLAG_FIRST_PIPE_INSTANCE: Final = 0x80000
FILE_FLAG_OVERLAPPED: Final = 0x40000000
FILE_GENERIC_READ: Literal[1179785]
FILE_GENERIC_WRITE: Literal[1179926]
FILE_GENERIC_READ: Final = 1179785
FILE_GENERIC_WRITE: Final = 1179926
FILE_MAP_ALL_ACCESS: Literal[983071]
FILE_MAP_COPY: Literal[1]
FILE_MAP_EXECUTE: Literal[32]
FILE_MAP_READ: Literal[4]
FILE_MAP_WRITE: Literal[2]
FILE_MAP_ALL_ACCESS: Final = 983071
FILE_MAP_COPY: Final = 1
FILE_MAP_EXECUTE: Final = 32
FILE_MAP_READ: Final = 4
FILE_MAP_WRITE: Final = 2
FILE_TYPE_CHAR: Literal[2]
FILE_TYPE_DISK: Literal[1]
FILE_TYPE_PIPE: Literal[3]
FILE_TYPE_REMOTE: Literal[32768]
FILE_TYPE_UNKNOWN: Literal[0]
FILE_TYPE_CHAR: Final = 2
FILE_TYPE_DISK: Final = 1
FILE_TYPE_PIPE: Final = 3
FILE_TYPE_REMOTE: Final = 32768
FILE_TYPE_UNKNOWN: Final = 0
GENERIC_READ: Literal[0x80000000]
GENERIC_WRITE: Literal[0x40000000]
HIGH_PRIORITY_CLASS: Literal[0x80]
INFINITE: Literal[0xFFFFFFFF]
GENERIC_READ: Final = 0x80000000
GENERIC_WRITE: Final = 0x40000000
HIGH_PRIORITY_CLASS: Final = 0x80
INFINITE: Final = 0xFFFFFFFF
# Ignore the Flake8 error -- flake8-pyi assumes
# most numbers this long will be implementation details,
# but here we can see that it's a power of 2
INVALID_HANDLE_VALUE: Literal[0xFFFFFFFFFFFFFFFF] # noqa: Y054
IDLE_PRIORITY_CLASS: Literal[0x40]
NORMAL_PRIORITY_CLASS: Literal[0x20]
REALTIME_PRIORITY_CLASS: Literal[0x100]
NMPWAIT_WAIT_FOREVER: Literal[0xFFFFFFFF]
INVALID_HANDLE_VALUE: Final = 0xFFFFFFFFFFFFFFFF # noqa: Y054
IDLE_PRIORITY_CLASS: Final = 0x40
NORMAL_PRIORITY_CLASS: Final = 0x20
REALTIME_PRIORITY_CLASS: Final = 0x100
NMPWAIT_WAIT_FOREVER: Final = 0xFFFFFFFF
MEM_COMMIT: Literal[0x1000]
MEM_FREE: Literal[0x10000]
MEM_IMAGE: Literal[0x1000000]
MEM_MAPPED: Literal[0x40000]
MEM_PRIVATE: Literal[0x20000]
MEM_RESERVE: Literal[0x2000]
MEM_COMMIT: Final = 0x1000
MEM_FREE: Final = 0x10000
MEM_IMAGE: Final = 0x1000000
MEM_MAPPED: Final = 0x40000
MEM_PRIVATE: Final = 0x20000
MEM_RESERVE: Final = 0x2000
NULL: Literal[0]
OPEN_EXISTING: Literal[3]
NULL: Final = 0
OPEN_EXISTING: Final = 3
PIPE_ACCESS_DUPLEX: Literal[3]
PIPE_ACCESS_INBOUND: Literal[1]
PIPE_READMODE_MESSAGE: Literal[2]
PIPE_TYPE_MESSAGE: Literal[4]
PIPE_UNLIMITED_INSTANCES: Literal[255]
PIPE_WAIT: Literal[0]
PIPE_ACCESS_DUPLEX: Final = 3
PIPE_ACCESS_INBOUND: Final = 1
PIPE_READMODE_MESSAGE: Final = 2
PIPE_TYPE_MESSAGE: Final = 4
PIPE_UNLIMITED_INSTANCES: Final = 255
PIPE_WAIT: Final = 0
PAGE_EXECUTE: Literal[0x10]
PAGE_EXECUTE_READ: Literal[0x20]
PAGE_EXECUTE_READWRITE: Literal[0x40]
PAGE_EXECUTE_WRITECOPY: Literal[0x80]
PAGE_GUARD: Literal[0x100]
PAGE_NOACCESS: Literal[0x1]
PAGE_NOCACHE: Literal[0x200]
PAGE_READONLY: Literal[0x2]
PAGE_READWRITE: Literal[0x4]
PAGE_WRITECOMBINE: Literal[0x400]
PAGE_WRITECOPY: Literal[0x8]
PAGE_EXECUTE: Final = 0x10
PAGE_EXECUTE_READ: Final = 0x20
PAGE_EXECUTE_READWRITE: Final = 0x40
PAGE_EXECUTE_WRITECOPY: Final = 0x80
PAGE_GUARD: Final = 0x100
PAGE_NOACCESS: Final = 0x1
PAGE_NOCACHE: Final = 0x200
PAGE_READONLY: Final = 0x2
PAGE_READWRITE: Final = 0x4
PAGE_WRITECOMBINE: Final = 0x400
PAGE_WRITECOPY: Final = 0x8
PROCESS_ALL_ACCESS: Literal[0x1FFFFF]
PROCESS_DUP_HANDLE: Literal[0x40]
PROCESS_ALL_ACCESS: Final = 0x1FFFFF
PROCESS_DUP_HANDLE: Final = 0x40
SEC_COMMIT: Literal[0x8000000]
SEC_IMAGE: Literal[0x1000000]
SEC_LARGE_PAGES: Literal[0x80000000]
SEC_NOCACHE: Literal[0x10000000]
SEC_RESERVE: Literal[0x4000000]
SEC_WRITECOMBINE: Literal[0x40000000]
SEC_COMMIT: Final = 0x8000000
SEC_IMAGE: Final = 0x1000000
SEC_LARGE_PAGES: Final = 0x80000000
SEC_NOCACHE: Final = 0x10000000
SEC_RESERVE: Final = 0x4000000
SEC_WRITECOMBINE: Final = 0x40000000
STARTF_USESHOWWINDOW: Literal[0x1]
STARTF_USESTDHANDLES: Literal[0x100]
STARTF_USESHOWWINDOW: Final = 0x1
STARTF_USESTDHANDLES: Final = 0x100
STD_ERROR_HANDLE: Literal[0xFFFFFFF4]
STD_OUTPUT_HANDLE: Literal[0xFFFFFFF5]
STD_INPUT_HANDLE: Literal[0xFFFFFFF6]
STD_ERROR_HANDLE: Final = 0xFFFFFFF4
STD_OUTPUT_HANDLE: Final = 0xFFFFFFF5
STD_INPUT_HANDLE: Final = 0xFFFFFFF6
STILL_ACTIVE: Literal[259]
SW_HIDE: Literal[0]
SYNCHRONIZE: Literal[0x100000]
WAIT_ABANDONED_0: Literal[128]
WAIT_OBJECT_0: Literal[0]
WAIT_TIMEOUT: Literal[258]
STILL_ACTIVE: Final = 259
SW_HIDE: Final = 0
SYNCHRONIZE: Final = 0x100000
WAIT_ABANDONED_0: Final = 128
WAIT_OBJECT_0: Final = 0
WAIT_TIMEOUT: Final = 258
if sys.version_info >= (3, 10):
LOCALE_NAME_INVARIANT: str
@@ -131,32 +131,32 @@ if sys.platform == "win32":
LCMAP_UPPERCASE: int
if sys.version_info >= (3, 12):
COPYFILE2_CALLBACK_CHUNK_STARTED: Literal[1]
COPYFILE2_CALLBACK_CHUNK_FINISHED: Literal[2]
COPYFILE2_CALLBACK_STREAM_STARTED: Literal[3]
COPYFILE2_CALLBACK_STREAM_FINISHED: Literal[4]
COPYFILE2_CALLBACK_POLL_CONTINUE: Literal[5]
COPYFILE2_CALLBACK_ERROR: Literal[6]
COPYFILE2_CALLBACK_CHUNK_STARTED: Final = 1
COPYFILE2_CALLBACK_CHUNK_FINISHED: Final = 2
COPYFILE2_CALLBACK_STREAM_STARTED: Final = 3
COPYFILE2_CALLBACK_STREAM_FINISHED: Final = 4
COPYFILE2_CALLBACK_POLL_CONTINUE: Final = 5
COPYFILE2_CALLBACK_ERROR: Final = 6
COPYFILE2_PROGRESS_CONTINUE: Literal[0]
COPYFILE2_PROGRESS_CANCEL: Literal[1]
COPYFILE2_PROGRESS_STOP: Literal[2]
COPYFILE2_PROGRESS_QUIET: Literal[3]
COPYFILE2_PROGRESS_PAUSE: Literal[4]
COPYFILE2_PROGRESS_CONTINUE: Final = 0
COPYFILE2_PROGRESS_CANCEL: Final = 1
COPYFILE2_PROGRESS_STOP: Final = 2
COPYFILE2_PROGRESS_QUIET: Final = 3
COPYFILE2_PROGRESS_PAUSE: Final = 4
COPY_FILE_FAIL_IF_EXISTS: Literal[0x1]
COPY_FILE_RESTARTABLE: Literal[0x2]
COPY_FILE_OPEN_SOURCE_FOR_WRITE: Literal[0x4]
COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Literal[0x8]
COPY_FILE_COPY_SYMLINK: Literal[0x800]
COPY_FILE_NO_BUFFERING: Literal[0x1000]
COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Literal[0x2000]
COPY_FILE_RESUME_FROM_PAUSE: Literal[0x4000]
COPY_FILE_NO_OFFLOAD: Literal[0x40000]
COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Literal[0x10000000]
COPY_FILE_FAIL_IF_EXISTS: Final = 0x1
COPY_FILE_RESTARTABLE: Final = 0x2
COPY_FILE_OPEN_SOURCE_FOR_WRITE: Final = 0x4
COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Final = 0x8
COPY_FILE_COPY_SYMLINK: Final = 0x800
COPY_FILE_NO_BUFFERING: Final = 0x1000
COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Final = 0x2000
COPY_FILE_RESUME_FROM_PAUSE: Final = 0x4000
COPY_FILE_NO_OFFLOAD: Final = 0x40000
COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Final = 0x10000000
ERROR_ACCESS_DENIED: Literal[5]
ERROR_PRIVILEGE_NOT_HELD: Literal[1314]
ERROR_ACCESS_DENIED: Final = 5
ERROR_PRIVILEGE_NOT_HELD: Final = 1314
def CloseHandle(handle: int, /) -> None: ...
@overload

View File

@@ -2,7 +2,7 @@ import sys
from _typeshed import sentinel
from collections.abc import Callable, Generator, Iterable, Sequence
from re import Pattern
from typing import IO, Any, Generic, Literal, NewType, NoReturn, Protocol, TypeVar, overload
from typing import IO, Any, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload
from typing_extensions import Self, TypeAlias, deprecated
__all__ = [
@@ -43,15 +43,15 @@ _ActionStr: TypeAlias = str
# callers that don't use a literal argument
_NArgsStr: TypeAlias = str
ONE_OR_MORE: Literal["+"]
OPTIONAL: Literal["?"]
PARSER: Literal["A..."]
REMAINDER: Literal["..."]
ONE_OR_MORE: Final = "+"
OPTIONAL: Final = "?"
PARSER: Final = "A..."
REMAINDER: Final = "..."
_SUPPRESS_T = NewType("_SUPPRESS_T", str)
SUPPRESS: _SUPPRESS_T | str # not using Literal because argparse sometimes compares SUPPRESS with is
# the | str is there so that foo = argparse.SUPPRESS; foo = "test" checks out in mypy
ZERO_OR_MORE: Literal["*"]
_UNRECOGNIZED_ARGS_ATTR: str # undocumented
ZERO_OR_MORE: Final = "*"
_UNRECOGNIZED_ARGS_ATTR: Final[str] # undocumented
class ArgumentError(Exception):
argument_name: str | None

View File

@@ -1,6 +1,6 @@
from collections.abc import Callable, Sequence
from contextvars import Context
from typing import Any, Literal
from typing import Any, Final
from . import futures
@@ -11,9 +11,9 @@ __all__ = ()
# That's why the import order is reversed.
from .futures import isfuture as isfuture
_PENDING: Literal["PENDING"] # undocumented
_CANCELLED: Literal["CANCELLED"] # undocumented
_FINISHED: Literal["FINISHED"] # undocumented
_PENDING: Final = "PENDING" # undocumented
_CANCELLED: Final = "CANCELLED" # undocumented
_FINISHED: Final = "FINISHED" # undocumented
def _format_callbacks(cb: Sequence[tuple[Callable[[futures.Future[Any]], None], Context]]) -> str: ... # undocumented
def _future_repr_info(future: futures.Future[Any]) -> list[str]: ... # undocumented

View File

@@ -1,18 +1,18 @@
import enum
import sys
from typing import Literal
from typing import Final
LOG_THRESHOLD_FOR_CONNLOST_WRITES: Literal[5]
ACCEPT_RETRY_DELAY: Literal[1]
DEBUG_STACK_DEPTH: Literal[10]
LOG_THRESHOLD_FOR_CONNLOST_WRITES: Final = 5
ACCEPT_RETRY_DELAY: Final = 1
DEBUG_STACK_DEPTH: Final = 10
SSL_HANDSHAKE_TIMEOUT: float
SENDFILE_FALLBACK_READBUFFER_SIZE: Literal[262144]
SENDFILE_FALLBACK_READBUFFER_SIZE: Final = 262144
if sys.version_info >= (3, 11):
SSL_SHUTDOWN_TIMEOUT: float
FLOW_CONTROL_HIGH_WATER_SSL_READ: Literal[256]
FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Literal[512]
FLOW_CONTROL_HIGH_WATER_SSL_READ: Final = 256
FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Final = 512
if sys.version_info >= (3, 12):
THREAD_JOIN_TIMEOUT: Literal[300]
THREAD_JOIN_TIMEOUT: Final = 300
class _SendfileMode(enum.Enum):
UNSUPPORTED = 1

View File

@@ -3,7 +3,7 @@ import sys
from collections import deque
from collections.abc import Callable
from enum import Enum
from typing import Any, ClassVar, Literal
from typing import Any, ClassVar, Final, Literal
from typing_extensions import TypeAlias
from . import constants, events, futures, protocols, transports
@@ -29,10 +29,10 @@ if sys.version_info >= (3, 11):
def add_flowcontrol_defaults(high: int | None, low: int | None, kb: int) -> tuple[int, int]: ...
else:
_UNWRAPPED: Literal["UNWRAPPED"]
_DO_HANDSHAKE: Literal["DO_HANDSHAKE"]
_WRAPPED: Literal["WRAPPED"]
_SHUTDOWN: Literal["SHUTDOWN"]
_UNWRAPPED: Final = "UNWRAPPED"
_DO_HANDSHAKE: Final = "DO_HANDSHAKE"
_WRAPPED: Final = "WRAPPED"
_SHUTDOWN: Final = "SHUTDOWN"
if sys.version_info < (3, 11):
class _SSLPipe:

View File

@@ -429,7 +429,11 @@ class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportIn
self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ...
) -> None: ...
def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ...
if sys.version_info >= (3, 12):
def get_coro(self) -> _TaskCompatibleCoro[_T_co] | None: ...
else:
def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ...
def get_name(self) -> str: ...
def set_name(self, value: object, /) -> None: ...
if sys.version_info >= (3, 12):

View File

@@ -2,7 +2,7 @@ import socket
import sys
from _typeshed import Incomplete, ReadableBuffer, WriteableBuffer
from collections.abc import Callable
from typing import IO, Any, ClassVar, Literal, NoReturn
from typing import IO, Any, ClassVar, Final, NoReturn
from . import events, futures, proactor_events, selector_events, streams, windows_utils
@@ -28,10 +28,10 @@ if sys.platform == "win32":
"WindowsProactorEventLoopPolicy",
)
NULL: Literal[0]
INFINITE: Literal[0xFFFFFFFF]
ERROR_CONNECTION_REFUSED: Literal[1225]
ERROR_CONNECTION_ABORTED: Literal[1236]
NULL: Final = 0
INFINITE: Final = 0xFFFFFFFF
ERROR_CONNECTION_REFUSED: Final = 1225
ERROR_CONNECTION_ABORTED: Final = 1236
CONNECT_PIPE_INIT_DELAY: float
CONNECT_PIPE_MAX_DELAY: float

View File

@@ -2,13 +2,13 @@ import subprocess
import sys
from collections.abc import Callable
from types import TracebackType
from typing import Any, AnyStr, Literal
from typing import Any, AnyStr, Final
from typing_extensions import Self
if sys.platform == "win32":
__all__ = ("pipe", "Popen", "PIPE", "PipeHandle")
BUFSIZE: Literal[8192]
BUFSIZE: Final = 8192
PIPE = subprocess.PIPE
STDOUT = subprocess.STDOUT
def pipe(*, duplex: bool = False, overlapped: tuple[bool, bool] = (True, True), bufsize: int = 8192) -> tuple[int, int]: ...

View File

@@ -2,7 +2,7 @@ import sys
from _typeshed import ExcInfo, TraceFunction, Unused
from collections.abc import Callable, Iterable, Mapping
from types import CodeType, FrameType, TracebackType
from typing import IO, Any, Literal, SupportsInt, TypeVar
from typing import IO, Any, Final, SupportsInt, TypeVar
from typing_extensions import ParamSpec
__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
@@ -10,7 +10,10 @@ __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
_T = TypeVar("_T")
_P = ParamSpec("_P")
GENERATOR_AND_COROUTINE_FLAGS: Literal[672]
# A union of code-object flags at runtime.
# The exact values of code-object flags are implementation details,
# so we don't include the value of this constant in the stubs.
GENERATOR_AND_COROUTINE_FLAGS: Final[int]
class BdbQuit(Exception): ...

View File

@@ -1,14 +1,14 @@
from _typeshed import SizedBuffer
from typing import IO, Any, Literal
from typing import IO, Any, Final
from typing_extensions import TypeAlias
__all__ = ["binhex", "hexbin", "Error"]
class Error(Exception): ...
REASONABLY_LARGE: Literal[32768]
LINELEN: Literal[64]
RUNCHAR: Literal[b"\x90"]
REASONABLY_LARGE: Final = 32768
LINELEN: Final = 64
RUNCHAR: Final = b"\x90"
class FInfo:
Type: str

View File

@@ -1868,6 +1868,7 @@ class BaseException:
__suppress_context__: bool
__traceback__: TracebackType | None
def __init__(self, *args: object) -> None: ...
def __new__(cls, *args: Any, **kwds: Any) -> Self: ...
def __setstate__(self, state: dict[str, Any] | None, /) -> None: ...
def with_traceback(self, tb: TracebackType | None, /) -> Self: ...
if sys.version_info >= (3, 11):

View File

@@ -1,9 +1,9 @@
from collections.abc import Callable
from typing import IO, Any, Literal
from typing import IO, Any, Final
__all__ = ["Cmd"]
PROMPT: Literal["(Cmd) "]
PROMPT: Final = "(Cmd) "
IDENTCHARS: str # Too big to be `Literal`
class Cmd:

View File

@@ -3,7 +3,7 @@ from _codecs import *
from _typeshed import ReadableBuffer
from abc import abstractmethod
from collections.abc import Callable, Generator, Iterable
from typing import Any, BinaryIO, Literal, Protocol, TextIO
from typing import Any, BinaryIO, Final, Literal, Protocol, TextIO
from typing_extensions import Self
__all__ = [
@@ -53,10 +53,10 @@ __all__ = [
"lookup_error",
]
BOM32_BE: Literal[b"\xfe\xff"]
BOM32_LE: Literal[b"\xff\xfe"]
BOM64_BE: Literal[b"\x00\x00\xfe\xff"]
BOM64_LE: Literal[b"\xff\xfe\x00\x00"]
BOM32_BE: Final = b"\xfe\xff"
BOM32_LE: Final = b"\xff\xfe"
BOM64_BE: Final = b"\x00\x00\xfe\xff"
BOM64_LE: Final = b"\xff\xfe\x00\x00"
class _WritableStream(Protocol):
def write(self, data: bytes, /) -> object: ...
@@ -135,23 +135,23 @@ def EncodedFile(file: _Stream, data_encoding: str, file_encoding: str | None = N
def iterencode(iterator: Iterable[str], encoding: str, errors: str = "strict") -> Generator[bytes, None, None]: ...
def iterdecode(iterator: Iterable[bytes], encoding: str, errors: str = "strict") -> Generator[str, None, None]: ...
BOM: Literal[b"\xff\xfe", b"\xfe\xff"] # depends on `sys.byteorder`
BOM_BE: Literal[b"\xfe\xff"]
BOM_LE: Literal[b"\xff\xfe"]
BOM_UTF8: Literal[b"\xef\xbb\xbf"]
BOM_UTF16: Literal[b"\xff\xfe", b"\xfe\xff"] # depends on `sys.byteorder`
BOM_UTF16_BE: Literal[b"\xfe\xff"]
BOM_UTF16_LE: Literal[b"\xff\xfe"]
BOM_UTF32: Literal[b"\xff\xfe\x00\x00", b"\x00\x00\xfe\xff"] # depends on `sys.byteorder`
BOM_UTF32_BE: Literal[b"\x00\x00\xfe\xff"]
BOM_UTF32_LE: Literal[b"\xff\xfe\x00\x00"]
BOM: Final[Literal[b"\xff\xfe", b"\xfe\xff"]] # depends on `sys.byteorder`
BOM_BE: Final = b"\xfe\xff"
BOM_LE: Final = b"\xff\xfe"
BOM_UTF8: Final = b"\xef\xbb\xbf"
BOM_UTF16: Final[Literal[b"\xff\xfe", b"\xfe\xff"]] # depends on `sys.byteorder`
BOM_UTF16_BE: Final = b"\xfe\xff"
BOM_UTF16_LE: Final = b"\xff\xfe"
BOM_UTF32: Final[Literal[b"\xff\xfe\x00\x00", b"\x00\x00\xfe\xff"]] # depends on `sys.byteorder`
BOM_UTF32_BE: Final = b"\x00\x00\xfe\xff"
BOM_UTF32_LE: Final = b"\xff\xfe\x00\x00"
def strict_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ...
def replace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ...
def ignore_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ...
def xmlcharrefreplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ...
def backslashreplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ...
def namereplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ...
def strict_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ...
def replace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ...
def ignore_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ...
def xmlcharrefreplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ...
def backslashreplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ...
def namereplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ...
class Codec:
# These are sort of @abstractmethod but sort of not.

View File

@@ -4,20 +4,20 @@ from _typeshed import Unused
from collections.abc import Callable, Collection, Iterable, Iterator
from logging import Logger
from types import TracebackType
from typing import Any, Generic, Literal, NamedTuple, Protocol, TypeVar
from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar
from typing_extensions import ParamSpec, Self
if sys.version_info >= (3, 9):
from types import GenericAlias
FIRST_COMPLETED: Literal["FIRST_COMPLETED"]
FIRST_EXCEPTION: Literal["FIRST_EXCEPTION"]
ALL_COMPLETED: Literal["ALL_COMPLETED"]
PENDING: Literal["PENDING"]
RUNNING: Literal["RUNNING"]
CANCELLED: Literal["CANCELLED"]
CANCELLED_AND_NOTIFIED: Literal["CANCELLED_AND_NOTIFIED"]
FINISHED: Literal["FINISHED"]
FIRST_COMPLETED: Final = "FIRST_COMPLETED"
FIRST_EXCEPTION: Final = "FIRST_EXCEPTION"
ALL_COMPLETED: Final = "ALL_COMPLETED"
PENDING: Final = "PENDING"
RUNNING: Final = "RUNNING"
CANCELLED: Final = "CANCELLED"
CANCELLED_AND_NOTIFIED: Final = "CANCELLED_AND_NOTIFIED"
FINISHED: Final = "FINISHED"
_FUTURE_STATES: list[str]
_STATE_TO_DESCRIPTION_MAP: dict[str, str]
LOGGER: Logger

View File

@@ -2,7 +2,7 @@ import sys
from _typeshed import StrOrBytesPath, SupportsWrite
from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence
from re import Pattern
from typing import Any, ClassVar, Literal, TypeVar, overload
from typing import Any, ClassVar, Final, Literal, TypeVar, overload
from typing_extensions import TypeAlias
if sys.version_info >= (3, 13):
@@ -83,8 +83,8 @@ _ConverterCallback: TypeAlias = Callable[[str], Any]
_ConvertersMap: TypeAlias = dict[str, _ConverterCallback]
_T = TypeVar("_T")
DEFAULTSECT: Literal["DEFAULT"]
MAX_INTERPOLATION_DEPTH: Literal[10]
DEFAULTSECT: Final = "DEFAULT"
MAX_INTERPOLATION_DEPTH: Final = 10
class Interpolation:
def before_get(self, parser: _Parser, section: str, option: str, value: str, defaults: _Section) -> str: ...

View File

@@ -1,8 +1,16 @@
from typing import Any, TypeVar
import sys
from typing import Any, Protocol, TypeVar
from typing_extensions import ParamSpec, Self
__all__ = ["Error", "copy", "deepcopy"]
_T = TypeVar("_T")
_SR = TypeVar("_SR", bound=_SupportsReplace[Any])
_P = ParamSpec("_P")
class _SupportsReplace(Protocol[_P]):
# In reality doesn't support args, but there's no other great way to express this.
def __replace__(self, *args: _P.args, **kwargs: _P.kwargs) -> Self: ...
# None in CPython but non-None in Jython
PyStringMap: Any
@@ -11,6 +19,10 @@ PyStringMap: Any
def deepcopy(x: _T, memo: dict[int, Any] | None = None, _nil: Any = []) -> _T: ...
def copy(x: _T) -> _T: ...
if sys.version_info >= (3, 13):
__all__ += ["replace"]
def replace(obj: _SR, /, **changes: Any) -> _SR: ...
class Error(Exception): ...
error = Error

View File

@@ -1,7 +1,7 @@
import sys
from abc import abstractmethod
from time import struct_time
from typing import ClassVar, Literal, NamedTuple, NoReturn, SupportsIndex, final, overload
from typing import ClassVar, Final, NamedTuple, NoReturn, SupportsIndex, final, overload
from typing_extensions import Self, TypeAlias, deprecated
if sys.version_info >= (3, 11):
@@ -9,8 +9,8 @@ if sys.version_info >= (3, 11):
elif sys.version_info >= (3, 9):
__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR")
MINYEAR: Literal[1]
MAXYEAR: Literal[9999]
MINYEAR: Final = 1
MAXYEAR: Final = 9999
class tzinfo:
@abstractmethod

View File

@@ -1,10 +1,11 @@
from _typeshed import BytesPath, StrPath
from _typeshed import BytesPath, StrPath, Unused
from collections.abc import Callable, Iterable
from distutils.file_util import _BytesPathT, _StrPathT
from typing import Any, Literal, overload
from typing_extensions import TypeAlias
from typing import Literal, overload
from typing_extensions import TypeAlias, TypeVarTuple, Unpack
_Macro: TypeAlias = tuple[str] | tuple[str, str | None]
_Ts = TypeVarTuple("_Ts")
def gen_lib_options(
compiler: CCompiler, library_dirs: list[str], runtime_library_dirs: list[str], libraries: list[str]
@@ -161,7 +162,9 @@ class CCompiler:
def shared_object_filename(self, basename: str, strip_dir: Literal[0, False] = 0, output_dir: StrPath = "") -> str: ...
@overload
def shared_object_filename(self, basename: StrPath, strip_dir: Literal[1, True], output_dir: StrPath = "") -> str: ...
def execute(self, func: Callable[..., object], args: tuple[Any, ...], msg: str | None = None, level: int = 1) -> None: ...
def execute(
self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1
) -> None: ...
def spawn(self, cmd: list[str]) -> None: ...
def mkpath(self, name: str, mode: int = 0o777) -> None: ...
@overload

View File

@@ -3,7 +3,11 @@ from abc import abstractmethod
from collections.abc import Callable, Iterable
from distutils.dist import Distribution
from distutils.file_util import _BytesPathT, _StrPathT
from typing import Any, ClassVar, Literal, overload
from typing import Any, ClassVar, Literal, TypeVar, overload
from typing_extensions import TypeVarTuple, Unpack
_CommandT = TypeVar("_CommandT", bound=Command)
_Ts = TypeVarTuple("_Ts")
class Command:
distribution: Distribution
@@ -19,17 +23,22 @@ class Command:
def announce(self, msg: str, level: int = 1) -> None: ...
def debug_print(self, msg: str) -> None: ...
def ensure_string(self, option: str, default: str | None = None) -> None: ...
def ensure_string_list(self, option: str | list[str]) -> None: ...
def ensure_string_list(self, option: str) -> None: ...
def ensure_filename(self, option: str) -> None: ...
def ensure_dirname(self, option: str) -> None: ...
def get_command_name(self) -> str: ...
def set_undefined_options(self, src_cmd: str, *option_pairs: tuple[str, str]) -> None: ...
def get_finalized_command(self, command: str, create: bool | Literal[0, 1] = 1) -> Command: ...
def reinitialize_command(self, command: Command | str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ...
@overload
def reinitialize_command(self, command: str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ...
@overload
def reinitialize_command(self, command: _CommandT, reinit_subcommands: bool | Literal[0, 1] = 0) -> _CommandT: ...
def run_command(self, command: str) -> None: ...
def get_sub_commands(self) -> list[str]: ...
def warn(self, msg: str) -> None: ...
def execute(self, func: Callable[..., object], args: Iterable[Any], msg: str | None = None, level: int = 1) -> None: ...
def execute(
self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1
) -> None: ...
def mkpath(self, name: str, mode: int = 0o777) -> None: ...
@overload
def copy_file(
@@ -89,8 +98,8 @@ class Command:
self,
infiles: str | list[str] | tuple[str, ...],
outfile: StrOrBytesPath,
func: Callable[..., object],
args: list[Any],
func: Callable[[Unpack[_Ts]], Unused],
args: tuple[Unpack[_Ts]],
exec_msg: str | None = None,
skip_msg: str | None = None,
level: Unused = 1,

View File

@@ -1,4 +1,6 @@
from typing import Any
from _typeshed import Unused
from collections.abc import Callable
from typing import Any, ClassVar
from ..cmd import Command
@@ -6,13 +8,13 @@ def show_formats() -> None: ...
class bdist(Command):
description: str
user_options: Any
boolean_options: Any
help_options: Any
no_format_option: Any
default_format: Any
format_commands: Any
format_command: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]]
no_format_option: ClassVar[tuple[str, ...]]
default_format: ClassVar[dict[str, str]]
format_commands: ClassVar[list[str]]
format_command: ClassVar[dict[str, tuple[str, str]]]
bdist_base: Any
plat_name: Any
formats: Any

View File

@@ -1,12 +1,12 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
class bdist_dumb(Command):
description: str
user_options: Any
boolean_options: Any
default_format: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
default_format: ClassVar[dict[str, str]]
bdist_dir: Any
plat_name: Any
format: Any

View File

@@ -1,5 +1,5 @@
import sys
from typing import Any, Literal
from typing import Any, ClassVar, Literal
from ..cmd import Command
@@ -16,8 +16,8 @@ if sys.platform == "win32":
class bdist_msi(Command):
description: str
user_options: Any
boolean_options: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
all_versions: Any
other_version: str
if sys.version_info >= (3, 9):

View File

@@ -1,12 +1,12 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
class bdist_rpm(Command):
description: str
user_options: Any
boolean_options: Any
negative_opt: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
negative_opt: ClassVar[dict[str, str]]
bdist_base: Any
rpm_base: Any
dist_dir: Any

View File

@@ -1,10 +1,10 @@
from _typeshed import StrOrBytesPath
from distutils.cmd import Command
from typing import Any, ClassVar
from typing import ClassVar
class bdist_wininst(Command):
description: ClassVar[str]
user_options: ClassVar[list[tuple[Any, ...]]]
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
def initialize_options(self) -> None: ...

View File

@@ -1,3 +1,4 @@
from _typeshed import Unused
from collections.abc import Callable
from typing import Any, ClassVar
@@ -7,9 +8,9 @@ def show_compilers() -> None: ...
class build(Command):
description: str
user_options: Any
boolean_options: Any
help_options: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]]
build_base: str
build_purelib: Any
build_platlib: Any

View File

@@ -1,4 +1,6 @@
from typing import Any
from _typeshed import Unused
from collections.abc import Callable
from typing import Any, ClassVar
from ..cmd import Command
@@ -6,9 +8,9 @@ def show_compilers() -> None: ...
class build_clib(Command):
description: str
user_options: Any
boolean_options: Any
help_options: Any
user_options: ClassVar[list[tuple[str, str, str]]]
boolean_options: ClassVar[list[str]]
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]]
build_clib: Any
build_temp: Any
libraries: Any

View File

@@ -1,4 +1,6 @@
from typing import Any
from _typeshed import Unused
from collections.abc import Callable
from typing import Any, ClassVar
from ..cmd import Command
@@ -9,9 +11,9 @@ def show_compilers() -> None: ...
class build_ext(Command):
description: str
sep_by: Any
user_options: Any
boolean_options: Any
help_options: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]]
extensions: Any
build_lib: Any
plat_name: Any

View File

@@ -1,13 +1,13 @@
from typing import Any, Literal
from typing import Any, ClassVar, Literal
from ..cmd import Command
from ..util import Mixin2to3 as Mixin2to3
class build_py(Command):
description: str
user_options: Any
boolean_options: Any
negative_opt: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
negative_opt: ClassVar[dict[str, str]]
build_lib: Any
py_modules: Any
package: Any

View File

@@ -1,4 +1,4 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
from ..util import Mixin2to3 as Mixin2to3
@@ -7,8 +7,8 @@ first_line_re: Any
class build_scripts(Command):
description: str
user_options: Any
boolean_options: Any
user_options: ClassVar[list[tuple[str, str, str]]]
boolean_options: ClassVar[list[str]]
build_dir: Any
scripts: Any
force: Any

View File

@@ -1,4 +1,4 @@
from typing import Any, Literal
from typing import Any, ClassVar, Literal
from typing_extensions import TypeAlias
from ..cmd import Command
@@ -26,8 +26,8 @@ HAS_DOCUTILS: bool
class check(Command):
description: str
user_options: Any
boolean_options: Any
user_options: ClassVar[list[tuple[str, str, str]]]
boolean_options: ClassVar[list[str]]
restructuredtext: int
metadata: int
strict: int

View File

@@ -1,11 +1,11 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
class clean(Command):
description: str
user_options: Any
boolean_options: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
build_base: Any
build_lib: Any
build_temp: Any

View File

@@ -1,7 +1,7 @@
from _typeshed import StrOrBytesPath
from collections.abc import Sequence
from re import Pattern
from typing import Any, Literal
from typing import Any, ClassVar, Literal
from ..ccompiler import CCompiler
from ..cmd import Command
@@ -11,7 +11,7 @@ LANG_EXT: dict[str, str]
class config(Command):
description: str
# Tuple is full name, short name, description
user_options: Sequence[tuple[str, str | None, str]]
user_options: ClassVar[list[tuple[str, str | None, str]]]
compiler: str | CCompiler
cc: str | None
include_dirs: Sequence[str] | None

View File

@@ -9,9 +9,9 @@ INSTALL_SCHEMES: dict[str, dict[Any, Any]]
class install(Command):
description: str
user_options: Any
boolean_options: Any
negative_opt: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
negative_opt: ClassVar[dict[str, str]]
prefix: str | None
exec_prefix: Any
home: str | None

View File

@@ -1,11 +1,11 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
class install_data(Command):
description: str
user_options: Any
boolean_options: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
install_dir: Any
outfiles: Any
root: Any

View File

@@ -4,7 +4,7 @@ from ..cmd import Command
class install_egg_info(Command):
description: ClassVar[str]
user_options: ClassVar[list[tuple[str, str | None, str]]]
user_options: ClassVar[list[tuple[str, str, str]]]
install_dir: Any
def initialize_options(self) -> None: ...
target: Any

View File

@@ -1,11 +1,11 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
class install_headers(Command):
description: str
user_options: Any
boolean_options: Any
user_options: ClassVar[list[tuple[str, str, str]]]
boolean_options: ClassVar[list[str]]
install_dir: Any
force: int
outfiles: Any

View File

@@ -1,4 +1,4 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
@@ -6,9 +6,9 @@ PYTHON_SOURCE_EXTENSION: str
class install_lib(Command):
description: str
user_options: Any
boolean_options: Any
negative_opt: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
negative_opt: ClassVar[dict[str, str]]
install_dir: Any
build_dir: Any
force: int

View File

@@ -1,11 +1,11 @@
from typing import Any
from typing import Any, ClassVar
from ..cmd import Command
class install_scripts(Command):
description: str
user_options: Any
boolean_options: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
install_dir: Any
force: int
build_dir: Any

View File

@@ -1,3 +1,4 @@
from _typeshed import Unused
from collections.abc import Callable
from typing import Any, ClassVar
@@ -8,13 +9,13 @@ def show_formats() -> None: ...
class sdist(Command):
description: str
def checking_metadata(self): ...
user_options: Any
boolean_options: Any
help_options: Any
negative_opt: Any
user_options: ClassVar[list[tuple[str, str | None, str]]]
boolean_options: ClassVar[list[str]]
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]]
negative_opt: ClassVar[dict[str, str]]
# Any to work around variance issues
sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]]
READMES: Any
READMES: ClassVar[tuple[str, ...]]
template: Any
manifest: Any
use_defaults: int

View File

@@ -1,8 +1,8 @@
from _typeshed import Incomplete, StrOrBytesPath, StrPath, SupportsWrite
from collections.abc import Iterable, Mapping
from collections.abc import Iterable, MutableMapping
from distutils.cmd import Command
from re import Pattern
from typing import IO, Any, ClassVar, Literal, TypeVar, overload
from typing import IO, ClassVar, Literal, TypeVar, overload
from typing_extensions import TypeAlias
command_re: Pattern[str]
@@ -60,7 +60,7 @@ class DistributionMetadata:
class Distribution:
cmdclass: dict[str, type[Command]]
metadata: DistributionMetadata
def __init__(self, attrs: Mapping[str, Any] | None = None) -> None: ...
def __init__(self, attrs: MutableMapping[str, Incomplete] | None = None) -> None: ...
def get_option_dict(self, command: str) -> dict[str, tuple[str, str]]: ...
def parse_config_files(self, filenames: Iterable[str] | None = None) -> None: ...
@overload

View File

@@ -1,6 +1,9 @@
from _typeshed import StrPath, Unused
from collections.abc import Callable, Container, Iterable, Mapping
from typing import Any, Literal
from typing_extensions import TypeVarTuple, Unpack
_Ts = TypeVarTuple("_Ts")
def get_host_platform() -> str: ...
def get_platform() -> str: ...
@@ -10,8 +13,8 @@ def check_environ() -> None: ...
def subst_vars(s: str, local_vars: Mapping[str, str]) -> None: ...
def split_quoted(s: str) -> list[str]: ...
def execute(
func: Callable[..., object],
args: tuple[Any, ...],
func: Callable[[Unpack[_Ts]], Unused],
args: tuple[Unpack[_Ts]],
msg: str | None = None,
verbose: bool | Literal[0, 1] = 0,
dry_run: bool | Literal[0, 1] = 0,

View File

@@ -1,12 +1,12 @@
from collections.abc import Callable, Iterator
from email.message import Message
from typing import overload
from typing import Final, overload
__all__ = ["Charset", "add_alias", "add_charset", "add_codec"]
QP: int # undocumented
BASE64: int # undocumented
SHORTEST: int # undocumented
QP: Final[int] # undocumented
BASE64: Final[int] # undocumented
SHORTEST: Final[int] # undocumented
class Charset:
input_charset: str

View File

@@ -1,6 +1,6 @@
import sys
from _typeshed import FileDescriptorLike, ReadOnlyBuffer, WriteableBuffer
from typing import Any, Literal, overload
from typing import Any, Final, Literal, overload
from typing_extensions import Buffer
if sys.platform != "win32":
@@ -44,9 +44,10 @@ if sys.platform != "win32":
F_SEAL_SHRINK: int
F_SEAL_WRITE: int
if sys.version_info >= (3, 9):
F_OFD_GETLK: int
F_OFD_SETLK: int
F_OFD_SETLKW: int
F_OFD_GETLK: Final[int]
F_OFD_SETLK: Final[int]
F_OFD_SETLKW: Final[int]
if sys.version_info >= (3, 10):
F_GETPIPE_SZ: int
F_SETPIPE_SZ: int
@@ -105,6 +106,36 @@ if sys.platform != "win32":
FICLONE: int
FICLONERANGE: int
if sys.version_info >= (3, 13) and sys.platform == "linux":
F_OWNER_TID: Final = 0
F_OWNER_PID: Final = 1
F_OWNER_PGRP: Final = 2
F_SETOWN_EX: Final = 15
F_GETOWN_EX: Final = 16
F_SEAL_FUTURE_WRITE: Final = 16
F_GET_RW_HINT: Final = 1035
F_SET_RW_HINT: Final = 1036
F_GET_FILE_RW_HINT: Final = 1037
F_SET_FILE_RW_HINT: Final = 1038
RWH_WRITE_LIFE_NOT_SET: Final = 0
RWH_WRITE_LIFE_NONE: Final = 1
RWH_WRITE_LIFE_SHORT: Final = 2
RWH_WRITE_LIFE_MEDIUM: Final = 3
RWH_WRITE_LIFE_LONG: Final = 4
RWH_WRITE_LIFE_EXTREME: Final = 5
if sys.version_info >= (3, 11) and sys.platform == "darwin":
F_OFD_SETLK: Final = 90
F_OFD_SETLKW: Final = 91
F_OFD_GETLK: Final = 92
if sys.version_info >= (3, 13) and sys.platform != "linux":
# OSx and NetBSD
F_GETNOSIGPIPE: Final[int]
F_SETNOSIGPIPE: Final[int]
# OSx and FreeBSD
F_RDAHEAD: Final[int]
@overload
def fcntl(fd: FileDescriptorLike, cmd: int, arg: int = 0, /) -> int: ...
@overload

View File

@@ -1,7 +1,7 @@
import sys
from _typeshed import GenericPath, StrOrBytesPath
from collections.abc import Callable, Iterable, Sequence
from typing import Any, AnyStr, Generic, Literal
from typing import Any, AnyStr, Final, Generic, Literal
if sys.version_info >= (3, 9):
from types import GenericAlias
@@ -9,7 +9,7 @@ if sys.version_info >= (3, 9):
__all__ = ["clear_cache", "cmp", "dircmp", "cmpfiles", "DEFAULT_IGNORES"]
DEFAULT_IGNORES: list[str]
BUFSIZE: Literal[8192]
BUFSIZE: Final = 8192
def cmp(f1: StrOrBytesPath, f2: StrOrBytesPath, shallow: bool | Literal[0, 1] = True) -> bool: ...
def cmpfiles(

View File

@@ -4,16 +4,16 @@ from collections.abc import Callable, Iterable, Iterator
from socket import socket
from ssl import SSLContext
from types import TracebackType
from typing import Any, Literal, TextIO
from typing import Any, Final, Literal, TextIO
from typing_extensions import Self
__all__ = ["FTP", "error_reply", "error_temp", "error_perm", "error_proto", "all_errors", "FTP_TLS"]
MSG_OOB: Literal[1]
FTP_PORT: Literal[21]
MAXLINE: Literal[8192]
CRLF: Literal["\r\n"]
B_CRLF: Literal[b"\r\n"]
MSG_OOB: Final = 1
FTP_PORT: Final = 21
MAXLINE: Final = 8192
CRLF: Final = "\r\n"
B_CRLF: Final = b"\r\n"
class Error(Exception): ...
class error_reply(Error): ...

View File

@@ -1,13 +1,13 @@
import sys
from collections.abc import Callable
from typing import Any, Literal
from typing import Any, Final, Literal
from typing_extensions import TypeAlias
DEBUG_COLLECTABLE: Literal[2]
DEBUG_LEAK: Literal[38]
DEBUG_SAVEALL: Literal[32]
DEBUG_STATS: Literal[1]
DEBUG_UNCOLLECTABLE: Literal[4]
DEBUG_COLLECTABLE: Final = 2
DEBUG_LEAK: Final = 38
DEBUG_SAVEALL: Final = 32
DEBUG_STATS: Final = 1
DEBUG_UNCOLLECTABLE: Final = 4
_CallbackType: TypeAlias = Callable[[Literal["start", "stop"], dict[str, int]], object]
@@ -34,4 +34,4 @@ if sys.version_info >= (3, 9):
def isenabled() -> bool: ...
def set_debug(flags: int, /) -> None: ...
def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ...) -> None: ...
def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ..., /) -> None: ...

View File

@@ -3,7 +3,7 @@ import sys
import zlib
from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath
from io import FileIO
from typing import Literal, Protocol, TextIO, overload
from typing import Final, Literal, Protocol, TextIO, overload
from typing_extensions import TypeAlias
__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]
@@ -12,14 +12,14 @@ _ReadBinaryMode: TypeAlias = Literal["r", "rb"]
_WriteBinaryMode: TypeAlias = Literal["a", "ab", "w", "wb", "x", "xb"]
_OpenTextMode: TypeAlias = Literal["rt", "at", "wt", "xt"]
READ: object # undocumented
WRITE: object # undocumented
READ: Final[object] # undocumented
WRITE: Final[object] # undocumented
FTEXT: int # actually Literal[1] # undocumented
FHCRC: int # actually Literal[2] # undocumented
FEXTRA: int # actually Literal[4] # undocumented
FNAME: int # actually Literal[8] # undocumented
FCOMMENT: int # actually Literal[16] # undocumented
FTEXT: Final[int] # actually Literal[1] # undocumented
FHCRC: Final[int] # actually Literal[2] # undocumented
FEXTRA: Final[int] # actually Literal[4] # undocumented
FNAME: Final[int] # actually Literal[8] # undocumented
FCOMMENT: Final[int] # actually Literal[16] # undocumented
class _ReadableFileobj(Protocol):
def read(self, n: int, /) -> bytes: ...

View File

@@ -42,7 +42,7 @@ class CookieJar(Iterable[Cookie]):
def __len__(self) -> int: ...
class FileCookieJar(CookieJar):
filename: str
filename: str | None
delayload: bool
def __init__(self, filename: StrPath | None = None, delayload: bool = False, policy: CookiePolicy | None = None) -> None: ...
def save(self, filename: str | None = None, ignore_discard: bool = False, ignore_expires: bool = False) -> None: ...

View File

@@ -25,7 +25,7 @@ from types import (
TracebackType,
WrapperDescriptorType,
)
from typing import Any, ClassVar, Literal, NamedTuple, Protocol, TypeVar, overload
from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload
from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs
if sys.version_info >= (3, 11):
@@ -161,17 +161,17 @@ class BlockFinder:
last: int
def tokeneater(self, type: int, token: str, srowcol: tuple[int, int], erowcol: tuple[int, int], line: str) -> None: ...
CO_OPTIMIZED: Literal[1]
CO_NEWLOCALS: Literal[2]
CO_VARARGS: Literal[4]
CO_VARKEYWORDS: Literal[8]
CO_NESTED: Literal[16]
CO_GENERATOR: Literal[32]
CO_NOFREE: Literal[64]
CO_COROUTINE: Literal[128]
CO_ITERABLE_COROUTINE: Literal[256]
CO_ASYNC_GENERATOR: Literal[512]
TPFLAGS_IS_ABSTRACT: Literal[1048576]
CO_OPTIMIZED: Final = 1
CO_NEWLOCALS: Final = 2
CO_VARARGS: Final = 4
CO_VARKEYWORDS: Final = 8
CO_NESTED: Final = 16
CO_GENERATOR: Final = 32
CO_NOFREE: Final = 64
CO_COROUTINE: Final = 128
CO_ITERABLE_COROUTINE: Final = 256
CO_ASYNC_GENERATOR: Final = 512
TPFLAGS_IS_ABSTRACT: Final = 1048576
modulesbyfile: dict[str, Any]
@@ -364,10 +364,10 @@ class _ParameterKind(enum.IntEnum):
def description(self) -> str: ...
if sys.version_info >= (3, 12):
AGEN_CREATED: Literal["AGEN_CREATED"]
AGEN_RUNNING: Literal["AGEN_RUNNING"]
AGEN_SUSPENDED: Literal["AGEN_SUSPENDED"]
AGEN_CLOSED: Literal["AGEN_CLOSED"]
AGEN_CREATED: Final = "AGEN_CREATED"
AGEN_RUNNING: Final = "AGEN_RUNNING"
AGEN_SUSPENDED: Final = "AGEN_SUSPENDED"
AGEN_CLOSED: Final = "AGEN_CLOSED"
def getasyncgenstate(
agen: AsyncGenerator[Any, Any]
@@ -584,19 +584,19 @@ def getattr_static(obj: object, attr: str, default: Any | None = ...) -> Any: ..
# Current State of Generators and Coroutines
#
GEN_CREATED: Literal["GEN_CREATED"]
GEN_RUNNING: Literal["GEN_RUNNING"]
GEN_SUSPENDED: Literal["GEN_SUSPENDED"]
GEN_CLOSED: Literal["GEN_CLOSED"]
GEN_CREATED: Final = "GEN_CREATED"
GEN_RUNNING: Final = "GEN_RUNNING"
GEN_SUSPENDED: Final = "GEN_SUSPENDED"
GEN_CLOSED: Final = "GEN_CLOSED"
def getgeneratorstate(
generator: Generator[Any, Any, Any]
) -> Literal["GEN_CREATED", "GEN_RUNNING", "GEN_SUSPENDED", "GEN_CLOSED"]: ...
CORO_CREATED: Literal["CORO_CREATED"]
CORO_RUNNING: Literal["CORO_RUNNING"]
CORO_SUSPENDED: Literal["CORO_SUSPENDED"]
CORO_CLOSED: Literal["CORO_CLOSED"]
CORO_CREATED: Final = "CORO_CREATED"
CORO_RUNNING: Final = "CORO_RUNNING"
CORO_SUSPENDED: Final = "CORO_SUSPENDED"
CORO_CLOSED: Final = "CORO_CLOSED"
def getcoroutinestate(
coroutine: Coroutine[Any, Any, Any]

View File

@@ -6,7 +6,7 @@ from _typeshed import FileDescriptorOrPath, ReadableBuffer, WriteableBuffer
from collections.abc import Callable, Iterable, Iterator
from os import _Opener
from types import TracebackType
from typing import IO, Any, BinaryIO, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only
from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only
from typing_extensions import Self
__all__ = [
@@ -36,11 +36,11 @@ if sys.version_info >= (3, 11):
_T = TypeVar("_T")
DEFAULT_BUFFER_SIZE: Literal[8192]
DEFAULT_BUFFER_SIZE: Final = 8192
SEEK_SET: Literal[0]
SEEK_CUR: Literal[1]
SEEK_END: Literal[2]
SEEK_SET: Final = 0
SEEK_CUR: Final = 1
SEEK_END: Final = 2
open = builtins.open
@@ -168,7 +168,7 @@ class _WrappedBuffer(Protocol):
def writable(self) -> bool: ...
def truncate(self, size: int, /) -> int: ...
def fileno(self) -> int: ...
def isatty(self) -> int: ...
def isatty(self) -> bool: ...
# Optional: Only needs to be present if seekable() returns True.
# def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ...
# def tell(self) -> int: ...

View File

@@ -1,11 +1,11 @@
import sys
from collections.abc import Iterable, Iterator
from typing import Any, Generic, Literal, SupportsInt, TypeVar, overload
from typing import Any, Final, Generic, Literal, SupportsInt, TypeVar, overload
from typing_extensions import Self, TypeAlias
# Undocumented length constants
IPV4LENGTH: Literal[32]
IPV6LENGTH: Literal[128]
IPV4LENGTH: Final = 32
IPV6LENGTH: Final = 128
_A = TypeVar("_A", IPv4Address, IPv6Address)
_N = TypeVar("_N", IPv4Network, IPv6Network)

View File

@@ -1,8 +1,8 @@
from typing import ClassVar, Literal
from typing import ClassVar, Final, Literal
from ..fixer_base import BaseFix
NAMES: dict[str, str]
NAMES: Final[dict[str, str]]
class FixAsserts(BaseFix):
BM_compatible: ClassVar[Literal[False]]

View File

@@ -1,9 +1,9 @@
from typing import ClassVar, Literal
from typing import ClassVar, Final, Literal
from .. import fixer_base
CMP: str
TYPE: str
CMP: Final[str]
TYPE: Final[str]
class FixIdioms(fixer_base.BaseFix):
BM_compatible: ClassVar[Literal[False]]

View File

@@ -1,11 +1,11 @@
from _typeshed import StrPath
from collections.abc import Generator
from typing import ClassVar, Literal
from typing import ClassVar, Final, Literal
from .. import fixer_base
from ..pytree import Node
MAPPING: dict[str, str]
MAPPING: Final[dict[str, str]]
def alternates(members): ...
def build_pattern(mapping=...) -> Generator[str, None, None]: ...

View File

@@ -1,6 +1,8 @@
from typing import Final
from . import fix_imports
MAPPING: dict[str, str]
MAPPING: Final[dict[str, str]]
class FixImports2(fix_imports.FixImports):
mapping = MAPPING

View File

@@ -1,8 +1,8 @@
from typing import ClassVar, Literal
from typing import ClassVar, Final, Literal
from .. import fixer_base
MAP: dict[str, str]
MAP: Final[dict[str, str]]
class FixMethodattrs(fixer_base.BaseFix):
BM_compatible: ClassVar[Literal[True]]

View File

@@ -1,10 +1,10 @@
from collections.abc import Generator
from typing import ClassVar, Literal
from typing import ClassVar, Final, Literal
from .. import fixer_base
MAPPING: dict[str, dict[str, str]]
LOOKUP: dict[tuple[str, str], str]
MAPPING: Final[dict[str, dict[str, str]]]
LOOKUP: Final[dict[tuple[str, str], str]]
def alternates(members): ...
def build_pattern() -> Generator[str, None, None]: ...

View File

@@ -1,9 +1,9 @@
from collections.abc import Generator
from typing import Literal
from typing import Final, Literal
from .fix_imports import FixImports
MAPPING: dict[str, list[tuple[Literal["urllib.request", "urllib.parse", "urllib.error"], list[str]]]]
MAPPING: Final[dict[str, list[tuple[Literal["urllib.request", "urllib.parse", "urllib.error"], list[str]]]]]
def build_pattern() -> Generator[str, None, None]: ...

View File

@@ -1,65 +1,67 @@
ENDMARKER: int
NAME: int
NUMBER: int
STRING: int
NEWLINE: int
INDENT: int
DEDENT: int
LPAR: int
RPAR: int
LSQB: int
RSQB: int
COLON: int
COMMA: int
SEMI: int
PLUS: int
MINUS: int
STAR: int
SLASH: int
VBAR: int
AMPER: int
LESS: int
GREATER: int
EQUAL: int
DOT: int
PERCENT: int
BACKQUOTE: int
LBRACE: int
RBRACE: int
EQEQUAL: int
NOTEQUAL: int
LESSEQUAL: int
GREATEREQUAL: int
TILDE: int
CIRCUMFLEX: int
LEFTSHIFT: int
RIGHTSHIFT: int
DOUBLESTAR: int
PLUSEQUAL: int
MINEQUAL: int
STAREQUAL: int
SLASHEQUAL: int
PERCENTEQUAL: int
AMPEREQUAL: int
VBAREQUAL: int
CIRCUMFLEXEQUAL: int
LEFTSHIFTEQUAL: int
RIGHTSHIFTEQUAL: int
DOUBLESTAREQUAL: int
DOUBLESLASH: int
DOUBLESLASHEQUAL: int
OP: int
COMMENT: int
NL: int
RARROW: int
AT: int
ATEQUAL: int
AWAIT: int
ASYNC: int
ERRORTOKEN: int
COLONEQUAL: int
N_TOKENS: int
NT_OFFSET: int
from typing import Final
ENDMARKER: Final[int]
NAME: Final[int]
NUMBER: Final[int]
STRING: Final[int]
NEWLINE: Final[int]
INDENT: Final[int]
DEDENT: Final[int]
LPAR: Final[int]
RPAR: Final[int]
LSQB: Final[int]
RSQB: Final[int]
COLON: Final[int]
COMMA: Final[int]
SEMI: Final[int]
PLUS: Final[int]
MINUS: Final[int]
STAR: Final[int]
SLASH: Final[int]
VBAR: Final[int]
AMPER: Final[int]
LESS: Final[int]
GREATER: Final[int]
EQUAL: Final[int]
DOT: Final[int]
PERCENT: Final[int]
BACKQUOTE: Final[int]
LBRACE: Final[int]
RBRACE: Final[int]
EQEQUAL: Final[int]
NOTEQUAL: Final[int]
LESSEQUAL: Final[int]
GREATEREQUAL: Final[int]
TILDE: Final[int]
CIRCUMFLEX: Final[int]
LEFTSHIFT: Final[int]
RIGHTSHIFT: Final[int]
DOUBLESTAR: Final[int]
PLUSEQUAL: Final[int]
MINEQUAL: Final[int]
STAREQUAL: Final[int]
SLASHEQUAL: Final[int]
PERCENTEQUAL: Final[int]
AMPEREQUAL: Final[int]
VBAREQUAL: Final[int]
CIRCUMFLEXEQUAL: Final[int]
LEFTSHIFTEQUAL: Final[int]
RIGHTSHIFTEQUAL: Final[int]
DOUBLESTAREQUAL: Final[int]
DOUBLESLASH: Final[int]
DOUBLESLASHEQUAL: Final[int]
OP: Final[int]
COMMENT: Final[int]
NL: Final[int]
RARROW: Final[int]
AT: Final[int]
ATEQUAL: Final[int]
AWAIT: Final[int]
ASYNC: Final[int]
ERRORTOKEN: Final[int]
COLONEQUAL: Final[int]
N_TOKENS: Final[int]
NT_OFFSET: Final[int]
tok_name: dict[int, str]
def ISTERMINAL(x: int) -> bool: ...

View File

@@ -7,7 +7,7 @@ from re import Pattern
from string import Template
from time import struct_time
from types import FrameType, TracebackType
from typing import Any, ClassVar, Generic, Literal, Protocol, TextIO, TypeVar, overload
from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload
from typing_extensions import Self, TypeAlias, deprecated
if sys.version_info >= (3, 11):
@@ -236,14 +236,14 @@ class Logger(Filterer):
def hasHandlers(self) -> bool: ...
def callHandlers(self, record: LogRecord) -> None: ... # undocumented
CRITICAL: int
FATAL: int
ERROR: int
WARNING: int
WARN: int
INFO: int
DEBUG: int
NOTSET: int
CRITICAL: Final = 50
FATAL: Final = CRITICAL
ERROR: Final = 40
WARNING: Final = 30
WARN: Final = WARNING
INFO: Final = 20
DEBUG: Final = 10
NOTSET: Final = 0
class Handler(Filterer):
level: int # undocumented
@@ -684,6 +684,6 @@ class StrFormatStyle(PercentStyle): # undocumented
class StringTemplateStyle(PercentStyle): # undocumented
_tpl: Template
_STYLES: dict[str, tuple[PercentStyle, str]]
_STYLES: Final[dict[str, tuple[PercentStyle, str]]]
BASIC_FORMAT: str
BASIC_FORMAT: Final[str]

View File

@@ -4,14 +4,14 @@ from collections.abc import Callable, Hashable, Iterable, Sequence
from configparser import RawConfigParser
from re import Pattern
from threading import Thread
from typing import IO, Any, Literal, SupportsIndex, TypedDict, overload
from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload
from typing_extensions import Required, TypeAlias
from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level
DEFAULT_LOGGING_CONFIG_PORT: int
RESET_ERROR: int # undocumented
IDENTIFIER: Pattern[str] # undocumented
RESET_ERROR: Final[int] # undocumented
IDENTIFIER: Final[Pattern[str]] # undocumented
if sys.version_info >= (3, 11):
class _RootLoggerConfiguration(TypedDict, total=False):

View File

@@ -8,16 +8,16 @@ from logging import FileHandler, Handler, LogRecord
from re import Pattern
from socket import SocketKind, socket
from threading import Thread
from typing import Any, ClassVar, Protocol, TypeVar
from typing import Any, ClassVar, Final, Protocol, TypeVar
_T = TypeVar("_T")
DEFAULT_TCP_LOGGING_PORT: int
DEFAULT_UDP_LOGGING_PORT: int
DEFAULT_HTTP_LOGGING_PORT: int
DEFAULT_SOAP_LOGGING_PORT: int
SYSLOG_UDP_PORT: int
SYSLOG_TCP_PORT: int
DEFAULT_TCP_LOGGING_PORT: Final[int]
DEFAULT_UDP_LOGGING_PORT: Final[int]
DEFAULT_HTTP_LOGGING_PORT: Final[int]
DEFAULT_SOAP_LOGGING_PORT: Final[int]
SYSLOG_UDP_PORT: Final[int]
SYSLOG_TCP_PORT: Final[int]
class WatchedFileHandler(FileHandler):
dev: int # undocumented

View File

@@ -1,7 +1,7 @@
from _compression import BaseStream
from _typeshed import ReadableBuffer, StrOrBytesPath
from collections.abc import Mapping, Sequence
from typing import IO, Any, Literal, TextIO, final, overload
from typing import IO, Any, Final, Literal, TextIO, final, overload
from typing_extensions import Self, TypeAlias
__all__ = [
@@ -50,33 +50,33 @@ _PathOrFile: TypeAlias = StrOrBytesPath | IO[bytes]
_FilterChain: TypeAlias = Sequence[Mapping[str, Any]]
FORMAT_AUTO: Literal[0]
FORMAT_XZ: Literal[1]
FORMAT_ALONE: Literal[2]
FORMAT_RAW: Literal[3]
CHECK_NONE: Literal[0]
CHECK_CRC32: Literal[1]
CHECK_CRC64: Literal[4]
CHECK_SHA256: Literal[10]
CHECK_ID_MAX: Literal[15]
CHECK_UNKNOWN: Literal[16]
FORMAT_AUTO: Final = 0
FORMAT_XZ: Final = 1
FORMAT_ALONE: Final = 2
FORMAT_RAW: Final = 3
CHECK_NONE: Final = 0
CHECK_CRC32: Final = 1
CHECK_CRC64: Final = 4
CHECK_SHA256: Final = 10
CHECK_ID_MAX: Final = 15
CHECK_UNKNOWN: Final = 16
FILTER_LZMA1: int # v big number
FILTER_LZMA2: Literal[33]
FILTER_DELTA: Literal[3]
FILTER_X86: Literal[4]
FILTER_IA64: Literal[6]
FILTER_ARM: Literal[7]
FILTER_ARMTHUMB: Literal[8]
FILTER_SPARC: Literal[9]
FILTER_POWERPC: Literal[5]
MF_HC3: Literal[3]
MF_HC4: Literal[4]
MF_BT2: Literal[18]
MF_BT3: Literal[19]
MF_BT4: Literal[20]
MODE_FAST: Literal[1]
MODE_NORMAL: Literal[2]
PRESET_DEFAULT: Literal[6]
FILTER_LZMA2: Final = 33
FILTER_DELTA: Final = 3
FILTER_X86: Final = 4
FILTER_IA64: Final = 6
FILTER_ARM: Final = 7
FILTER_ARMTHUMB: Final = 8
FILTER_SPARC: Final = 9
FILTER_POWERPC: Final = 5
MF_HC3: Final = 3
MF_HC4: Final = 4
MF_BT2: Final = 18
MF_BT3: Final = 19
MF_BT4: Final = 20
MODE_FAST: Final = 1
MODE_NORMAL: Final = 2
PRESET_DEFAULT: Final = 6
PRESET_EXTREME: int # v big number
# from _lzma.c

View File

@@ -118,4 +118,16 @@ if sys.version_info >= (3, 13) and sys.platform != "win32":
MAP_32BIT: Final = 32768
if sys.version_info >= (3, 13) and sys.platform == "darwin":
MAP_NORESERVE: Final = 64
MAP_NOEXTEND: Final = 256
MAP_HASSEMAPHORE: Final = 512
MAP_NOCACHE: Final = 1024
MAP_JIT: Final = 2048
MAP_RESILIENT_CODESIGN: Final = 8192
MAP_RESILIENT_MEDIA: Final = 16384
MAP_TRANSLATED_ALLOW_EXECUTE: Final = 131072
MAP_UNIX03: Final = 262144
MAP_TPRO: Final = 524288
if sys.version_info >= (3, 13) and sys.platform == "linux":
MAP_NORESERVE: Final = 16384

View File

@@ -1,15 +1,15 @@
import sys
from collections.abc import Container, Iterable, Iterator, Sequence
from types import CodeType
from typing import IO, Any
from typing import IO, Any, Final
if sys.version_info < (3, 11):
LOAD_CONST: int # undocumented
IMPORT_NAME: int # undocumented
STORE_NAME: int # undocumented
STORE_GLOBAL: int # undocumented
STORE_OPS: tuple[int, int] # undocumented
EXTENDED_ARG: int # undocumented
LOAD_CONST: Final[int] # undocumented
IMPORT_NAME: Final[int] # undocumented
STORE_NAME: Final[int] # undocumented
STORE_GLOBAL: Final[int] # undocumented
STORE_OPS: Final[tuple[int, int]] # undocumented
EXTENDED_ARG: Final[int] # undocumented
packagePathMap: dict[str, list[str]] # undocumented

View File

@@ -1,14 +1,14 @@
import sys
from typing import Final, Literal
from typing import Final
# This module is only available on Windows
if sys.platform == "win32":
CRT_ASSEMBLY_VERSION: Final[str]
LK_UNLCK: Literal[0]
LK_LOCK: Literal[1]
LK_NBLCK: Literal[2]
LK_RLCK: Literal[3]
LK_NBRLCK: Literal[4]
LK_UNLCK: Final = 0
LK_LOCK: Final = 1
LK_NBLCK: Final = 2
LK_RLCK: Final = 3
LK_NBRLCK: Final = 4
SEM_FAILCRITICALERRORS: int
SEM_NOALIGNMENTFAULTEXCEPT: int
SEM_NOGPFAULTERRORBOX: int

View File

@@ -1,12 +1,12 @@
from _typeshed import FileDescriptorLike, Unused
from collections.abc import Sequence
from struct import Struct
from typing import Any
from typing import Any, Final
__all__ = ["ensure_running", "get_inherited_fds", "connect_to_new_process", "set_forkserver_preload"]
MAXFDS_TO_SEND: int
SIGNED_STRUCT: Struct
MAXFDS_TO_SEND: Final = 256
SIGNED_STRUCT: Final[Struct]
class ForkServer:
def set_forkserver_preload(self, modules_names: list[str]) -> None: ...

View File

@@ -1,7 +1,7 @@
import sys
from collections.abc import Callable, Iterable, Iterator, Mapping
from types import TracebackType
from typing import Any, Generic, Literal, TypeVar
from typing import Any, Final, Generic, TypeVar
from typing_extensions import Self
if sys.version_info >= (3, 9):
@@ -97,7 +97,7 @@ class ThreadPool(Pool):
) -> None: ...
# undocumented
INIT: Literal["INIT"]
RUN: Literal["RUN"]
CLOSE: Literal["CLOSE"]
TERMINATE: Literal["TERMINATE"]
INIT: Final = "INIT"
RUN: Final = "RUN"
CLOSE: Final = "CLOSE"
TERMINATE: Final = "TERMINATE"

View File

@@ -1,16 +1,16 @@
import sys
from multiprocessing.process import BaseProcess
from typing import ClassVar
from typing import ClassVar, Final
from .util import Finalize
if sys.platform == "win32":
__all__ = ["Popen"]
TERMINATE: int
WINEXE: bool
WINSERVICE: bool
WINENV: bool
TERMINATE: Final[int]
WINEXE: Final[bool]
WINSERVICE: Final[bool]
WINENV: Final[bool]
class Popen:
finalizer: Finalize

View File

@@ -8,14 +8,14 @@ from copyreg import _DispatchTableType
from multiprocessing import connection
from pickle import _ReducedType
from socket import socket
from typing import Any, Literal
from typing import Any, Final
if sys.platform == "win32":
__all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupHandle", "duplicate", "steal_handle"]
else:
__all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupFd", "sendfds", "recvfds"]
HAVE_SEND_HANDLE: bool
HAVE_SEND_HANDLE: Final[bool]
class ForkingPickler(pickle.Pickler):
dispatch_table: _DispatchTableType
@@ -43,10 +43,7 @@ if sys.platform == "win32":
def detach(self) -> int: ...
else:
if sys.platform == "darwin":
ACKNOWLEDGE: Literal[True]
else:
ACKNOWLEDGE: Literal[False]
ACKNOWLEDGE: Final[bool]
def recvfds(sock: socket, size: int) -> list[int]: ...
def send_handle(conn: HasFileno, handle: int, destination_pid: Unused) -> None: ...

View File

@@ -1,6 +1,6 @@
from collections.abc import Mapping, Sequence
from types import ModuleType
from typing import Any
from typing import Any, Final
__all__ = [
"_main",
@@ -12,8 +12,8 @@ __all__ = [
"import_main_path",
]
WINEXE: bool
WINSERVICE: bool
WINEXE: Final[bool]
WINSERVICE: Final[bool]
def set_executable(exe: str) -> None: ...
def get_executable() -> str: ...

View File

@@ -2,7 +2,7 @@ import threading
from _typeshed import ConvertibleToInt, Incomplete, Unused
from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence
from logging import Logger, _Level as _LoggingLevel
from typing import Any, Generic, TypeVar, overload
from typing import Any, Final, Generic, TypeVar, overload
__all__ = [
"sub_debug",
@@ -25,14 +25,14 @@ __all__ = [
_T = TypeVar("_T")
_R_co = TypeVar("_R_co", default=Any, covariant=True)
NOTSET: int
SUBDEBUG: int
DEBUG: int
INFO: int
SUBWARNING: int
NOTSET: Final[int]
SUBDEBUG: Final[int]
DEBUG: Final[int]
INFO: Final[int]
SUBWARNING: Final[int]
LOGGER_NAME: str
DEFAULT_LOGGING_FORMAT: str
LOGGER_NAME: Final[str]
DEFAULT_LOGGING_FORMAT: Final[str]
def sub_debug(msg: object, *args: object) -> None: ...
def debug(msg: object, *args: object) -> None: ...
@@ -92,7 +92,7 @@ class ForkAwareThreadLock:
class ForkAwareLocal(threading.local): ...
MAXFD: int
MAXFD: Final[int]
def close_all_fds_except(fds: Iterable[int]) -> None: ...
def spawnv_passfds(path: bytes, args: Sequence[ConvertibleToInt], passfds: Sequence[int]) -> int: ...

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