Compare commits
7 Commits
0.5.2
...
simplify-S
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
827747ddcd | ||
|
|
e871037f7c | ||
|
|
957aba0be9 | ||
|
|
76e40b2ed3 | ||
|
|
a4c6246ad7 | ||
|
|
7e1309b854 | ||
|
|
0ae19efe74 |
@@ -1,10 +1,3 @@
|
||||
[alias]
|
||||
dev = "run --package ruff_dev --bin ruff_dev"
|
||||
benchmark = "bench -p ruff_benchmark --bench linter --bench formatter --"
|
||||
|
||||
# statically link the C runtime so the executable does not depend on
|
||||
# that shared/dynamic library.
|
||||
#
|
||||
# See: https://github.com/astral-sh/ruff/issues/11503
|
||||
[target.'cfg(all(target_env="msvc", target_os = "windows"))']
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
[profile.ci]
|
||||
# Print out output for failing tests as soon as they fail, and also at the end
|
||||
# of the run (for easy scrollability).
|
||||
failure-output = "immediate-final"
|
||||
# Do not cancel the test run on the first failure.
|
||||
fail-fast = false
|
||||
|
||||
status-level = "skip"
|
||||
8
.gitattributes
vendored
8
.gitattributes
vendored
@@ -2,17 +2,9 @@
|
||||
|
||||
crates/ruff_linter/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
|
||||
crates/ruff_linter/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
|
||||
crates/ruff_linter/resources/test/fixtures/pycodestyle/W391_2.py text eol=crlf
|
||||
crates/ruff_linter/resources/test/fixtures/pycodestyle/W391_3.py text eol=crlf
|
||||
|
||||
crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring_code_examples_crlf.py text eol=crlf
|
||||
crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap text eol=crlf
|
||||
|
||||
crates/ruff_python_parser/resources/invalid/re_lexing/line_continuation_windows_eol.py text eol=crlf
|
||||
crates/ruff_python_parser/resources/invalid/re_lex_logical_token_windows_eol.py text eol=crlf
|
||||
crates/ruff_python_parser/resources/invalid/re_lex_logical_token_mac_eol.py text eol=cr
|
||||
|
||||
crates/ruff_python_parser/resources/inline linguist-generated=true
|
||||
|
||||
ruff.schema.json linguist-generated=true text=auto eol=lf
|
||||
*.md.snap linguist-language=Markdown
|
||||
|
||||
16
.github/CODEOWNERS
vendored
16
.github/CODEOWNERS
vendored
@@ -5,17 +5,5 @@
|
||||
# - The '*' pattern is global owners.
|
||||
# - Order is important. The last matching pattern has the most precedence.
|
||||
|
||||
/crates/ruff_notebook/ @dhruvmanila
|
||||
/crates/ruff_formatter/ @MichaReiser
|
||||
/crates/ruff_python_formatter/ @MichaReiser
|
||||
/crates/ruff_python_parser/ @MichaReiser @dhruvmanila
|
||||
|
||||
# flake8-pyi
|
||||
/crates/ruff_linter/src/rules/flake8_pyi/ @AlexWaygood
|
||||
|
||||
# Script for fuzzing the parser
|
||||
/scripts/fuzz-parser/ @AlexWaygood
|
||||
|
||||
# red-knot
|
||||
/crates/red_knot* @carljm @MichaReiser @AlexWaygood
|
||||
/crates/ruff_db/ @carljm @MichaReiser @AlexWaygood
|
||||
# Jupyter
|
||||
/crates/ruff_linter/src/jupyter/ @dhruvmanila
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -3,8 +3,6 @@ Thank you for taking the time to report an issue! We're glad to have you involve
|
||||
|
||||
If you're filing a bug report, please consider including the following information:
|
||||
|
||||
* List of keywords you searched for before creating this issue. Write them down here so that others can find this issue more easily and help provide feedback.
|
||||
e.g. "RUF001", "unused variable", "Jupyter notebook"
|
||||
* A minimal code snippet that reproduces the bug.
|
||||
* The command you invoked (e.g., `ruff /path/to/file.py --fix`), ideally including the `--isolated` flag.
|
||||
* The current Ruff settings (any relevant sections from your `pyproject.toml`).
|
||||
|
||||
17
.github/dependabot.yml
vendored
Normal file
17
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels: ["internal"]
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels: ["internal"]
|
||||
86
.github/renovate.json5
vendored
86
.github/renovate.json5
vendored
@@ -1,86 +0,0 @@
|
||||
{
|
||||
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
||||
dependencyDashboard: true,
|
||||
suppressNotifications: ["prEditedNotification"],
|
||||
extends: ["config:recommended"],
|
||||
labels: ["internal"],
|
||||
schedule: ["before 4am on Monday"],
|
||||
semanticCommits: "disabled",
|
||||
separateMajorMinor: false,
|
||||
prHourlyLimit: 10,
|
||||
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "npm"],
|
||||
cargo: {
|
||||
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
|
||||
rangeStrategy: "update-lockfile",
|
||||
},
|
||||
pep621: {
|
||||
fileMatch: ["^(python|scripts)/.*pyproject\\.toml$"],
|
||||
},
|
||||
npm: {
|
||||
fileMatch: ["^playground/.*package\\.json$"],
|
||||
},
|
||||
"pre-commit": {
|
||||
enabled: true,
|
||||
},
|
||||
packageRules: [
|
||||
{
|
||||
// Group upload/download artifact updates, the versions are dependent
|
||||
groupName: "Artifact GitHub Actions dependencies",
|
||||
matchManagers: ["github-actions"],
|
||||
matchDatasources: ["gitea-tags", "github-tags"],
|
||||
matchPackagePatterns: ["actions/.*-artifact"],
|
||||
description: "Weekly update of artifact-related GitHub Actions dependencies",
|
||||
},
|
||||
{
|
||||
// This package rule disables updates for GitHub runners:
|
||||
// we'd only pin them to a specific version
|
||||
// if there was a deliberate reason to do so
|
||||
groupName: "GitHub runners",
|
||||
matchManagers: ["github-actions"],
|
||||
matchDatasources: ["github-runners"],
|
||||
description: "Disable PRs updating GitHub runners (e.g. 'runs-on: macos-14')",
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
// Disable updates of `zip-rs`; intentionally pinned for now due to ownership change
|
||||
// See: https://github.com/astral-sh/uv/issues/3642
|
||||
matchPackagePatterns: ["zip"],
|
||||
matchManagers: ["cargo"],
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
groupName: "pre-commit dependencies",
|
||||
matchManagers: ["pre-commit"],
|
||||
description: "Weekly update of pre-commit dependencies",
|
||||
},
|
||||
{
|
||||
groupName: "NPM Development dependencies",
|
||||
matchManagers: ["npm"],
|
||||
matchDepTypes: ["devDependencies"],
|
||||
description: "Weekly update of NPM development dependencies",
|
||||
},
|
||||
{
|
||||
groupName: "Monaco",
|
||||
matchManagers: ["npm"],
|
||||
matchPackagePatterns: ["monaco"],
|
||||
description: "Weekly update of the Monaco editor",
|
||||
},
|
||||
{
|
||||
groupName: "strum",
|
||||
matchManagers: ["cargo"],
|
||||
matchPackagePatterns: ["strum"],
|
||||
description: "Weekly update of strum dependencies",
|
||||
},
|
||||
{
|
||||
groupName: "ESLint",
|
||||
matchManagers: ["npm"],
|
||||
matchPackageNames: ["eslint"],
|
||||
allowedVersions: "<9",
|
||||
description: "Constraint ESLint to version 8 until TypeScript-eslint supports ESLint 9", // https://github.com/typescript-eslint/typescript-eslint/issues/8211
|
||||
},
|
||||
],
|
||||
vulnerabilityAlerts: {
|
||||
commitMessageSuffix: "",
|
||||
labels: ["internal", "security"],
|
||||
},
|
||||
}
|
||||
68
.github/workflows/build-docker.yml
vendored
68
.github/workflows/build-docker.yml
vendored
@@ -1,68 +0,0 @@
|
||||
# Build and publish a Docker image.
|
||||
#
|
||||
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a local
|
||||
# artifacts job within `cargo-dist`.
|
||||
#
|
||||
# TODO(charlie): Ideally, the publish step would happen as a publish job within `cargo-dist`, but
|
||||
# sharing the built image as an artifact between jobs is challenging.
|
||||
name: "[ruff] Build Docker image"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
plan:
|
||||
required: true
|
||||
type: string
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/build-docker.yml
|
||||
|
||||
jobs:
|
||||
docker-publish:
|
||||
name: Build Docker image (ghcr.io/astral-sh/ruff)
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/astral-sh/ruff
|
||||
|
||||
- name: Check tag consistency
|
||||
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
|
||||
run: |
|
||||
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g')
|
||||
if [ "${{ fromJson(inputs.plan).announcement_tag }}" != "${version}" ]; then
|
||||
echo "The input tag does not match the version from pyproject.toml:" >&2
|
||||
echo "${{ fromJson(inputs.plan).announcement_tag }}" >&2
|
||||
echo "${version}" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "Releasing ${version}"
|
||||
fi
|
||||
|
||||
- name: "Build and push Docker image"
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
# Reuse the builder
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
push: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
|
||||
tags: ghcr.io/astral-sh/ruff:latest,ghcr.io/astral-sh/ruff:${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || 'dry-run' }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
227
.github/workflows/ci.yaml
vendored
227
.github/workflows/ci.yaml
vendored
@@ -23,8 +23,6 @@ jobs:
|
||||
name: "Determine changes"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
# Flag that is raised when any code that affects parser is changed
|
||||
parser: ${{ steps.changed.outputs.parser_any_changed }}
|
||||
# Flag that is raised when any code that affects linter is changed
|
||||
linter: ${{ steps.changed.outputs.linter_any_changed }}
|
||||
# Flag that is raised when any code that affects formatter is changed
|
||||
@@ -37,21 +35,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: tj-actions/changed-files@v44
|
||||
- uses: tj-actions/changed-files@v40
|
||||
id: changed
|
||||
with:
|
||||
files_yaml: |
|
||||
parser:
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- crates/ruff_python_trivia/**
|
||||
- crates/ruff_source_file/**
|
||||
- crates/ruff_text_size/**
|
||||
- crates/ruff_python_ast/**
|
||||
- crates/ruff_python_parser/**
|
||||
- scripts/fuzz-parser/**
|
||||
- .github/workflows/ci.yaml
|
||||
|
||||
linter:
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
@@ -59,6 +46,7 @@ jobs:
|
||||
- "!crates/ruff_python_formatter/**"
|
||||
- "!crates/ruff_formatter/**"
|
||||
- "!crates/ruff_dev/**"
|
||||
- "!crates/ruff_shrinking/**"
|
||||
- scripts/*
|
||||
- python/**
|
||||
- .github/workflows/ci.yaml
|
||||
@@ -88,7 +76,6 @@ jobs:
|
||||
cargo-fmt:
|
||||
name: "cargo fmt"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -100,7 +87,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -114,21 +100,40 @@ jobs:
|
||||
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
|
||||
|
||||
cargo-test-linux:
|
||||
name: "cargo test (linux)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
name: "cargo test (linux)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@v1
|
||||
- name: "Install cargo nextest"
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
tool: cargo-insta
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Run tests"
|
||||
run: cargo insta test --all --all-features --unreferenced reject
|
||||
# Check for broken links in the documentation.
|
||||
- run: cargo doc --all --no-deps
|
||||
env:
|
||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
cargo-test-windows:
|
||||
runs-on: windows-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo test (windows)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
@@ -136,50 +141,14 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
NEXTEST_PROFILE: "ci"
|
||||
run: cargo insta test --all-features --unreferenced reject --test-runner nextest
|
||||
|
||||
# Check for broken links in the documentation.
|
||||
- run: cargo doc --all --no-deps
|
||||
env:
|
||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
cargo-test-windows:
|
||||
name: "cargo test (windows)"
|
||||
runs-on: windows-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
# Workaround for <https://github.com/nextest-rs/nextest/issues/1493>.
|
||||
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
|
||||
run: |
|
||||
cargo nextest run --all-features --profile ci
|
||||
cargo test --all-features --doc
|
||||
# We can't reject unreferenced snapshots on windows because flake8_executable can't run on windows
|
||||
run: cargo insta test --all --all-features
|
||||
|
||||
cargo-test-wasm:
|
||||
name: "cargo test (wasm)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 10
|
||||
name: "cargo test (wasm)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -196,60 +165,11 @@ jobs:
|
||||
cd crates/ruff_wasm
|
||||
wasm-pack test --node
|
||||
|
||||
cargo-build-release:
|
||||
name: "cargo build (release)"
|
||||
runs-on: macos-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Build"
|
||||
run: cargo build --release --locked
|
||||
|
||||
cargo-build-msrv:
|
||||
name: "cargo build (msrv)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: SebRollen/toml-action@v1.2.0
|
||||
id: msrv
|
||||
with:
|
||||
file: "Cargo.toml"
|
||||
field: "workspace.package.rust-version"
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup default ${{ steps.msrv.outputs.value }}
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
NEXTEST_PROFILE: "ci"
|
||||
run: cargo +${{ steps.msrv.outputs.value }} insta test --all-features --unreferenced reject --test-runner nextest
|
||||
|
||||
cargo-fuzz:
|
||||
name: "cargo fuzz"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 10
|
||||
name: "cargo fuzz"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -257,53 +177,17 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "fuzz -> target"
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@main
|
||||
- name: "Install cargo-fuzz"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-fuzz@0.11.2
|
||||
- name: "Install cargo-fuzz"
|
||||
# Download the latest version from quick install and not the github releases because github releases only has MUSL targets.
|
||||
run: cargo binstall cargo-fuzz --force --disable-strategies crate-meta-data --no-confirm
|
||||
- run: cargo fuzz build -s none
|
||||
|
||||
fuzz-parser:
|
||||
name: "Fuzz the parser"
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- cargo-test-linux
|
||||
- determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.parser == 'true' }}
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: Install uv
|
||||
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
- name: Install Python requirements
|
||||
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Download Ruff binary to test
|
||||
id: download-cached-binary
|
||||
with:
|
||||
name: ruff
|
||||
path: ruff-to-test
|
||||
- name: Fuzz
|
||||
run: |
|
||||
# Make executable, since artifact download doesn't preserve this
|
||||
chmod +x ${{ steps.download-cached-binary.outputs.download-path }}/ruff
|
||||
|
||||
python scripts/fuzz-parser/fuzz.py 0-500 --test-executable ${{ steps.download-cached-binary.outputs.download-path }}/ruff
|
||||
|
||||
scripts:
|
||||
name: "test scripts"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -326,27 +210,27 @@ jobs:
|
||||
- determine_changes
|
||||
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
|
||||
# Ecosystem check needs linter and/or formatter changes.
|
||||
if: ${{ github.event_name == 'pull_request' && needs.determine_changes.outputs.code == 'true' }}
|
||||
timeout-minutes: 20
|
||||
if: github.event_name == 'pull_request' && ${{
|
||||
needs.determine_changes.outputs.code == 'true'
|
||||
}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
name: Download comparison Ruff binary
|
||||
id: ruff-target
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v3
|
||||
name: Download baseline Ruff binary
|
||||
with:
|
||||
name: ruff
|
||||
branch: ${{ github.event.pull_request.base.ref }}
|
||||
workflow: "ci.yaml"
|
||||
check_artifacts: true
|
||||
|
||||
- name: Install ruff-ecosystem
|
||||
@@ -421,33 +305,37 @@ jobs:
|
||||
run: |
|
||||
echo ${{ github.event.number }} > pr-number
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload PR Number
|
||||
with:
|
||||
name: pr-number
|
||||
path: pr-number
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload Results
|
||||
with:
|
||||
name: ecosystem-result
|
||||
path: ecosystem-result
|
||||
|
||||
cargo-shear:
|
||||
name: "cargo shear"
|
||||
cargo-udeps:
|
||||
name: "cargo udeps"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cargo-bins/cargo-binstall@main
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
- name: "Install nightly Rust toolchain"
|
||||
# Only pinned to make caching work, update freely
|
||||
run: rustup toolchain install nightly-2023-10-15
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install cargo-udeps"
|
||||
uses: taiki-e/install-action@cargo-udeps
|
||||
- name: "Run cargo-udeps"
|
||||
run: cargo +nightly-2023-10-15 udeps
|
||||
|
||||
python-package:
|
||||
name: "python package"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
@@ -472,7 +360,6 @@ jobs:
|
||||
pre-commit:
|
||||
name: "pre-commit"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
@@ -484,7 +371,7 @@ jobs:
|
||||
- name: "Install pre-commit"
|
||||
run: pip install pre-commit
|
||||
- name: "Cache pre-commit"
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
@@ -501,7 +388,6 @@ jobs:
|
||||
docs:
|
||||
name: "mkdocs"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
@@ -509,7 +395,7 @@ jobs:
|
||||
- uses: actions/setup-python@v5
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -539,7 +425,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.formatter == 'true' || github.ref == 'refs/heads/main'
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -556,13 +441,12 @@ jobs:
|
||||
check-ruff-lsp:
|
||||
name: "test ruff-lsp"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
needs:
|
||||
- cargo-test-linux
|
||||
- determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: extractions/setup-just@v2
|
||||
- uses: extractions/setup-just@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -575,7 +459,7 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
name: Download development ruff binary
|
||||
id: ruff-target
|
||||
with:
|
||||
@@ -599,8 +483,7 @@ jobs:
|
||||
benchmarks:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ github.repository == 'astral-sh/ruff' && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
|
||||
timeout-minutes: 20
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- name: "Checkout Branch"
|
||||
uses: actions/checkout@v4
|
||||
|
||||
72
.github/workflows/daily_fuzz.yaml
vendored
72
.github/workflows/daily_fuzz.yaml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Daily parser fuzz
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/daily_fuzz.yaml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
PACKAGE_NAME: ruff
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
fuzz:
|
||||
name: Fuzz
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
# Don't run the cron job on forks:
|
||||
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install uv
|
||||
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
- name: Install Python requirements
|
||||
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Build ruff
|
||||
# A debug build means the script runs slower once it gets started,
|
||||
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
|
||||
run: cargo build --locked
|
||||
- name: Fuzz
|
||||
run: python scripts/fuzz-parser/fuzz.py $(shuf -i 0-9999999999999999999 -n 1000) --test-executable target/debug/ruff
|
||||
|
||||
create-issue-on-failure:
|
||||
name: Create an issue if the daily fuzz surfaced any bugs
|
||||
runs-on: ubuntu-latest
|
||||
needs: fuzz
|
||||
if: ${{ github.repository == 'astral-sh/ruff' && always() && github.event_name == 'schedule' && needs.fuzz.result == 'failure' }}
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.create({
|
||||
owner: "astral-sh",
|
||||
repo: "ruff",
|
||||
title: `Daily parser fuzz failed on ${new Date().toDateString()}`,
|
||||
body: "Runs listed here: https://github.com/astral-sh/ruff/actions/workflows/daily_fuzz.yml",
|
||||
labels: ["bug", "parser", "fuzzer"],
|
||||
})
|
||||
@@ -1,7 +1,3 @@
|
||||
# Publish the Ruff documentation.
|
||||
#
|
||||
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce
|
||||
# job within `cargo-dist`.
|
||||
name: mkdocs
|
||||
|
||||
on:
|
||||
@@ -11,11 +7,8 @@ on:
|
||||
description: "The commit SHA, tag, or branch to publish. Uses the default branch if not specified."
|
||||
default: ""
|
||||
type: string
|
||||
workflow_call:
|
||||
inputs:
|
||||
plan:
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
mkdocs:
|
||||
@@ -30,7 +23,7 @@ jobs:
|
||||
- uses: actions/setup-python@v5
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -54,7 +47,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.public.yml
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.7.0
|
||||
uses: cloudflare/wrangler-action@v3.3.2
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
29
.github/workflows/notify-dependents.yml
vendored
29
.github/workflows/notify-dependents.yml
vendored
@@ -1,29 +0,0 @@
|
||||
# Notify downstream repositories of a new release.
|
||||
#
|
||||
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce
|
||||
# job within `cargo-dist`.
|
||||
name: "[ruff] Notify dependents"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
plan:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
update-dependents:
|
||||
name: Notify dependents
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Update pre-commit mirror"
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.RUFF_PRE_COMMIT_PAT }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'astral-sh',
|
||||
repo: 'ruff-pre-commit',
|
||||
workflow_id: 'main.yml',
|
||||
ref: 'main',
|
||||
})
|
||||
@@ -1,16 +1,9 @@
|
||||
# Publish the Ruff playground.
|
||||
#
|
||||
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce
|
||||
# job within `cargo-dist`.
|
||||
name: "[Playground] Release"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
plan:
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -47,7 +40,7 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.7.0
|
||||
uses: cloudflare/wrangler-action@v3.3.2
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
16
.github/workflows/pr-comment.yaml
vendored
16
.github/workflows/pr-comment.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v3
|
||||
name: Download pull request number
|
||||
with:
|
||||
name: pr-number
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
echo "pr-number=$(<pr-number)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v3
|
||||
name: "Download ecosystem results"
|
||||
id: download-ecosystem-result
|
||||
if: steps.pr-number.outputs.pr-number
|
||||
@@ -48,14 +48,6 @@ jobs:
|
||||
id: generate-comment
|
||||
if: steps.download-ecosystem-result.outputs.found_artifact == 'true'
|
||||
run: |
|
||||
# Guard against malicious ecosystem results that symlink to a secret
|
||||
# file on this runner
|
||||
if [[ -L pr/ecosystem/ecosystem-result ]]
|
||||
then
|
||||
echo "Error: ecosystem-result cannot be a symlink"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Note this identifier is used to find the comment to update on
|
||||
# subsequent runs
|
||||
echo '<!-- generated-comment ecosystem -->' >> comment.txt
|
||||
@@ -69,7 +61,7 @@ jobs:
|
||||
echo 'EOF' >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Find existing comment
|
||||
uses: peter-evans/find-comment@v3
|
||||
uses: peter-evans/find-comment@v2
|
||||
if: steps.generate-comment.outcome == 'success'
|
||||
id: find-comment
|
||||
with:
|
||||
@@ -79,7 +71,7 @@ jobs:
|
||||
|
||||
- name: Create or update comment
|
||||
if: steps.find-comment.outcome == 'success'
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
comment-id: ${{ steps.find-comment.outputs.comment-id }}
|
||||
issue-number: ${{ steps.pr-number.outputs.pr-number }}
|
||||
|
||||
34
.github/workflows/publish-pypi.yml
vendored
34
.github/workflows/publish-pypi.yml
vendored
@@ -1,34 +0,0 @@
|
||||
# Publish a release to PyPI.
|
||||
#
|
||||
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a publish job
|
||||
# within `cargo-dist`.
|
||||
name: "[ruff] Publish to PyPI"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
plan:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: Upload to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
permissions:
|
||||
# For PyPI's trusted publishing.
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: wheels-*
|
||||
path: wheels
|
||||
merge-multiple: true
|
||||
- name: Publish to PyPi
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
skip-existing: true
|
||||
packages-dir: wheels
|
||||
verbose: true
|
||||
@@ -1,23 +1,21 @@
|
||||
# Build ruff on all platforms.
|
||||
#
|
||||
# Generates both wheels (for PyPI) and archived binaries (for GitHub releases).
|
||||
#
|
||||
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a local
|
||||
# artifacts job within `cargo-dist`.
|
||||
name: "Build binaries"
|
||||
name: "[ruff] Release"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
plan:
|
||||
required: true
|
||||
tag:
|
||||
description: "The version to tag, without the leading 'v'. If omitted, will initiate a dry run (no uploads)."
|
||||
type: string
|
||||
sha:
|
||||
description: "The full sha of the commit to be released. If omitted, the latest commit on the default branch will be used."
|
||||
default: ""
|
||||
type: string
|
||||
pull_request:
|
||||
paths:
|
||||
# When we change pyproject.toml, we want to ensure that the maturin builds still work.
|
||||
# When we change pyproject.toml, we want to ensure that the maturin builds still work
|
||||
- pyproject.toml
|
||||
# And when we change this workflow itself...
|
||||
- .github/workflows/build-binaries.yml
|
||||
- .github/workflows/release.yaml
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -25,7 +23,6 @@ concurrency:
|
||||
|
||||
env:
|
||||
PACKAGE_NAME: ruff
|
||||
MODULE_NAME: ruff
|
||||
PYTHON_VERSION: "3.11"
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
@@ -34,12 +31,11 @@ env:
|
||||
|
||||
jobs:
|
||||
sdist:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
@@ -53,21 +49,20 @@ jobs:
|
||||
- name: "Test sdist"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
|
||||
${{ env.MODULE_NAME }} --help
|
||||
python -m ${{ env.MODULE_NAME }} --help
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload sdist"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels-sdist
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
macos-x86_64:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: macos-12
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
@@ -79,77 +74,69 @@ jobs:
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --locked --out dist
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-macos-x86_64
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
TARGET=x86_64-apple-darwin
|
||||
ARCHIVE_NAME=ruff-$TARGET
|
||||
ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz
|
||||
|
||||
mkdir -p $ARCHIVE_NAME
|
||||
cp target/$TARGET/release/ruff $ARCHIVE_NAME/ruff
|
||||
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: artifacts-macos-x86_64
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
macos-aarch64:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: arm64
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - aarch64"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: aarch64
|
||||
args: --release --locked --out dist
|
||||
- name: "Test wheel - aarch64"
|
||||
- name: "Test wheel - x86_64"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels-aarch64-apple-darwin
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
TARGET=aarch64-apple-darwin
|
||||
ARCHIVE_NAME=ruff-$TARGET
|
||||
ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz
|
||||
|
||||
mkdir -p $ARCHIVE_NAME
|
||||
cp target/$TARGET/release/ruff $ARCHIVE_NAME/ruff
|
||||
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-x86_64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/x86_64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts-aarch64-apple-darwin
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
macos-universal:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - universal2"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --locked --target universal2-apple-darwin --out dist
|
||||
- name: "Test wheel - universal2"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-aarch64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/aarch64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
windows:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -163,7 +150,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
@@ -175,37 +162,33 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --locked --out dist
|
||||
env:
|
||||
# aarch64 build fails, see https://github.com/PyO3/maturin/issues/2110
|
||||
XWIN_VERSION: 16
|
||||
- name: "Test wheel"
|
||||
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
${{ env.MODULE_NAME }} --help
|
||||
python -m ${{ env.MODULE_NAME }} --help
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels-${{ matrix.platform.target }}
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.zip
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.zip
|
||||
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
|
||||
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts-${{ matrix.platform.target }}
|
||||
name: binaries
|
||||
path: |
|
||||
*.zip
|
||||
*.sha256
|
||||
|
||||
linux:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -215,7 +198,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
@@ -232,36 +215,27 @@ jobs:
|
||||
if: ${{ startsWith(matrix.target, 'x86_64') }}
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
${{ env.MODULE_NAME }} --help
|
||||
python -m ${{ env.MODULE_NAME }} --help
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels-${{ matrix.target }}
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
TARGET=${{ matrix.target }}
|
||||
ARCHIVE_NAME=ruff-$TARGET
|
||||
ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz
|
||||
|
||||
mkdir -p $ARCHIVE_NAME
|
||||
cp target/$TARGET/release/ruff $ARCHIVE_NAME/ruff
|
||||
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts-${{ matrix.target }}
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
linux-cross:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -277,19 +251,13 @@ jobs:
|
||||
arch: s390x
|
||||
- target: powerpc64le-unknown-linux-gnu
|
||||
arch: ppc64le
|
||||
# see https://github.com/astral-sh/ruff/issues/10073
|
||||
maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16
|
||||
- target: powerpc64-unknown-linux-gnu
|
||||
arch: ppc64
|
||||
# see https://github.com/astral-sh/ruff/issues/10073
|
||||
maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16
|
||||
- target: arm-unknown-linux-musleabihf
|
||||
arch: arm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
@@ -306,8 +274,8 @@ jobs:
|
||||
if: matrix.platform.arch != 'ppc64'
|
||||
name: Test wheel
|
||||
with:
|
||||
arch: ${{ matrix.platform.arch == 'arm' && 'armv6' || matrix.platform.arch }}
|
||||
distro: ${{ matrix.platform.arch == 'arm' && 'bullseye' || 'ubuntu20.04' }}
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
distro: ubuntu20.04
|
||||
githubToken: ${{ github.token }}
|
||||
install: |
|
||||
apt-get update
|
||||
@@ -317,33 +285,24 @@ jobs:
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
ruff --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels-${{ matrix.platform.target }}
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
TARGET=${{ matrix.platform.target }}
|
||||
ARCHIVE_NAME=ruff-$TARGET
|
||||
ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz
|
||||
|
||||
mkdir -p $ARCHIVE_NAME
|
||||
cp target/$TARGET/release/ruff $ARCHIVE_NAME/ruff
|
||||
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts-${{ matrix.platform.target }}
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
musllinux:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -353,7 +312,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
@@ -376,35 +335,26 @@ jobs:
|
||||
apk add python3
|
||||
python -m venv .venv
|
||||
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
.venv/bin/${{ env.MODULE_NAME }} --help
|
||||
.venv/bin/ruff check --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels-${{ matrix.target }}
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
TARGET=${{ matrix.target }}
|
||||
ARCHIVE_NAME=ruff-$TARGET
|
||||
ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz
|
||||
|
||||
mkdir -p $ARCHIVE_NAME
|
||||
cp target/$TARGET/release/ruff $ARCHIVE_NAME/ruff
|
||||
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts-${{ matrix.target }}
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
musllinux-cross:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -418,7 +368,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
@@ -442,29 +392,202 @@ jobs:
|
||||
run: |
|
||||
python -m venv .venv
|
||||
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
.venv/bin/${{ env.MODULE_NAME }} --help
|
||||
.venv/bin/ruff check --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels-${{ matrix.platform.target }}
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
TARGET=${{ matrix.platform.target }}
|
||||
ARCHIVE_NAME=ruff-$TARGET
|
||||
ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz
|
||||
|
||||
mkdir -p $ARCHIVE_NAME
|
||||
cp target/$TARGET/release/ruff $ARCHIVE_NAME/ruff
|
||||
tar czvf $ARCHIVE_FILE $ARCHIVE_NAME
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts-${{ matrix.platform.target }}
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
validate-tag:
|
||||
name: Validate tag
|
||||
runs-on: ubuntu-latest
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main # We checkout the main branch to check for the commit
|
||||
- name: Check main branch
|
||||
if: ${{ inputs.sha }}
|
||||
run: |
|
||||
# Fetch the main branch since a shallow checkout is used by default
|
||||
git fetch origin main --unshallow
|
||||
if ! git branch --contains ${{ inputs.sha }} | grep -E '(^|\s)main$'; then
|
||||
echo "The specified sha is not on the main branch" >&2
|
||||
exit 1
|
||||
fi
|
||||
- name: Check tag consistency
|
||||
run: |
|
||||
# Switch to the commit we want to release
|
||||
git checkout ${{ inputs.sha }}
|
||||
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g')
|
||||
if [ "${{ inputs.tag }}" != "${version}" ]; then
|
||||
echo "The input tag does not match the version from pyproject.toml:" >&2
|
||||
echo "${{ inputs.tag }}" >&2
|
||||
echo "${version}" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "Releasing ${version}"
|
||||
fi
|
||||
|
||||
upload-release:
|
||||
name: Upload to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- macos-universal
|
||||
- macos-x86_64
|
||||
- windows
|
||||
- linux
|
||||
- linux-cross
|
||||
- musllinux
|
||||
- musllinux-cross
|
||||
- validate-tag
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
environment:
|
||||
name: release
|
||||
permissions:
|
||||
# For pypi trusted publishing
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: wheels
|
||||
- name: Publish to PyPi
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
skip-existing: true
|
||||
packages-dir: wheels
|
||||
verbose: true
|
||||
|
||||
tag-release:
|
||||
name: Tag release
|
||||
runs-on: ubuntu-latest
|
||||
needs: upload-release
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
permissions:
|
||||
# For git tag
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- name: git tag
|
||||
run: |
|
||||
git config user.email "hey@astral.sh"
|
||||
git config user.name "Ruff Release CI"
|
||||
git tag -m "v${{ inputs.tag }}" "v${{ inputs.tag }}"
|
||||
# If there is duplicate tag, this will fail. The publish to pypi action will have been a noop (due to skip
|
||||
# existing), so we make a non-destructive exit here
|
||||
git push --tags
|
||||
|
||||
publish-release:
|
||||
name: Publish to GitHub
|
||||
runs-on: ubuntu-latest
|
||||
needs: tag-release
|
||||
# If you don't set an input tag, it's a dry run (no uploads).
|
||||
if: ${{ inputs.tag }}
|
||||
permissions:
|
||||
# For GitHub release publishing
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: binaries
|
||||
- name: "Publish to GitHub"
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: binaries/*
|
||||
tag_name: v${{ inputs.tag }}
|
||||
|
||||
docker-publish:
|
||||
# This action doesn't need to wait on any other task, it's easy to re-tag if something failed and we're validating
|
||||
# the tag here also
|
||||
name: Push Docker image ghcr.io/astral-sh/ruff
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
permissions:
|
||||
# For the docker push
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/astral-sh/ruff
|
||||
|
||||
- name: Check tag consistency
|
||||
# Unlike validate-tag we don't check if the commit is on the main branch, but it seems good enough since we can
|
||||
# change docker tags
|
||||
if: ${{ inputs.tag }}
|
||||
run: |
|
||||
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g')
|
||||
if [ "${{ inputs.tag }}" != "${version}" ]; then
|
||||
echo "The input tag does not match the version from pyproject.toml:" >&2
|
||||
echo "${{ inputs.tag }}" >&2
|
||||
echo "${version}" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "Releasing ${version}"
|
||||
fi
|
||||
|
||||
- name: "Build and push Docker image"
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
# Reuse the builder
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
push: ${{ inputs.tag != '' }}
|
||||
tags: ghcr.io/astral-sh/ruff:latest,ghcr.io/astral-sh/ruff:${{ inputs.tag || 'dry-run' }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
# After the release has been published, we update downstream repositories
|
||||
# This is separate because if this fails the release is still fine, we just need to do some manual workflow triggers
|
||||
update-dependents:
|
||||
name: Update dependents
|
||||
runs-on: ubuntu-latest
|
||||
needs: publish-release
|
||||
steps:
|
||||
- name: "Update pre-commit mirror"
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.RUFF_PRE_COMMIT_PAT }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'astral-sh',
|
||||
repo: 'ruff-pre-commit',
|
||||
workflow_id: 'main.yml',
|
||||
ref: 'main',
|
||||
})
|
||||
282
.github/workflows/release.yml
vendored
282
.github/workflows/release.yml
vendored
@@ -1,282 +0,0 @@
|
||||
# Copyright 2022-2024, axodotdev
|
||||
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||
#
|
||||
# CI that:
|
||||
#
|
||||
# * checks for a Git Tag that looks like a release
|
||||
# * builds artifacts with cargo-dist (archives, installers, hashes)
|
||||
# * uploads those artifacts to temporary workflow zip
|
||||
# * on success, uploads the artifacts to a GitHub Release
|
||||
#
|
||||
# Note that the GitHub Release will be created with a generated
|
||||
# title/body based on your changelogs.
|
||||
|
||||
name: Release
|
||||
permissions:
|
||||
"contents": "write"
|
||||
|
||||
# This task will run whenever you workflow_dispatch with a tag that looks like a version
|
||||
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
|
||||
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
|
||||
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
|
||||
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
|
||||
#
|
||||
# If PACKAGE_NAME is specified, then the announcement will be for that
|
||||
# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
|
||||
#
|
||||
# If PACKAGE_NAME isn't specified, then the announcement will be for all
|
||||
# (cargo-dist-able) packages in the workspace with that version (this mode is
|
||||
# intended for workspaces with only one dist-able package, or with all dist-able
|
||||
# packages versioned/released in lockstep).
|
||||
#
|
||||
# If you push multiple tags at once, separate instances of this workflow will
|
||||
# spin up, creating an independent announcement for each one. However, GitHub
|
||||
# will hard limit this to 3 tags per commit, as it will assume more tags is a
|
||||
# mistake.
|
||||
#
|
||||
# If there's a prerelease-style suffix to the version, then the release(s)
|
||||
# will be marked as a prerelease.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Release Tag
|
||||
required: true
|
||||
default: dry-run
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
# Run 'cargo dist plan' (or host) to determine what tasks we need to do
|
||||
plan:
|
||||
runs-on: "ubuntu-20.04"
|
||||
outputs:
|
||||
val: ${{ steps.plan.outputs.manifest }}
|
||||
tag: ${{ (inputs.tag != 'dry-run' && inputs.tag) || '' }}
|
||||
tag-flag: ${{ inputs.tag && inputs.tag != 'dry-run' && format('--tag={0}', inputs.tag) || '' }}
|
||||
publishing: ${{ inputs.tag && inputs.tag != 'dry-run' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cargo-dist
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.18.0/cargo-dist-installer.sh | sh"
|
||||
- name: Cache cargo-dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/cargo-dist
|
||||
# sure would be cool if github gave us proper conditionals...
|
||||
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
|
||||
# functionality based on whether this is a pull_request, and whether it's from a fork.
|
||||
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
|
||||
# but also really annoying to build CI around when it needs secrets to work right.)
|
||||
- id: plan
|
||||
run: |
|
||||
cargo dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||
echo "cargo dist ran successfully"
|
||||
cat plan-dist-manifest.json
|
||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: artifacts-plan-dist-manifest
|
||||
path: plan-dist-manifest.json
|
||||
|
||||
custom-build-binaries:
|
||||
needs:
|
||||
- plan
|
||||
if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }}
|
||||
uses: ./.github/workflows/build-binaries.yml
|
||||
with:
|
||||
plan: ${{ needs.plan.outputs.val }}
|
||||
secrets: inherit
|
||||
|
||||
custom-build-docker:
|
||||
needs:
|
||||
- plan
|
||||
if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }}
|
||||
uses: ./.github/workflows/build-docker.yml
|
||||
with:
|
||||
plan: ${{ needs.plan.outputs.val }}
|
||||
secrets: inherit
|
||||
permissions:
|
||||
"contents": "read"
|
||||
"packages": "write"
|
||||
|
||||
# Build and package all the platform-agnostic(ish) things
|
||||
build-global-artifacts:
|
||||
needs:
|
||||
- plan
|
||||
- custom-build-binaries
|
||||
- custom-build-docker
|
||||
runs-on: "ubuntu-20.04"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cached cargo-dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/cargo-dist
|
||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
- id: cargo-dist
|
||||
shell: bash
|
||||
run: |
|
||||
cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||
echo "cargo dist ran successfully"
|
||||
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: artifacts-build-global
|
||||
path: |
|
||||
${{ steps.cargo-dist.outputs.paths }}
|
||||
${{ env.BUILD_MANIFEST_NAME }}
|
||||
# Determines if we should publish/announce
|
||||
host:
|
||||
needs:
|
||||
- plan
|
||||
- custom-build-binaries
|
||||
- custom-build-docker
|
||||
- build-global-artifacts
|
||||
# Only run if we're "publishing", and only if local and global didn't fail (skipped is fine)
|
||||
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-binaries.result == 'skipped' || needs.custom-build-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
runs-on: "ubuntu-20.04"
|
||||
outputs:
|
||||
val: ${{ steps.host.outputs.manifest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cached cargo-dist
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/cargo-dist
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
# This is a harmless no-op for GitHub Releases, hosting for that happens in "announce"
|
||||
- id: host
|
||||
shell: bash
|
||||
run: |
|
||||
cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||
echo "artifacts uploaded and released successfully"
|
||||
cat dist-manifest.json
|
||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
# Overwrite the previous copy
|
||||
name: artifacts-dist-manifest
|
||||
path: dist-manifest.json
|
||||
|
||||
custom-publish-pypi:
|
||||
needs:
|
||||
- plan
|
||||
- host
|
||||
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
|
||||
uses: ./.github/workflows/publish-pypi.yml
|
||||
with:
|
||||
plan: ${{ needs.plan.outputs.val }}
|
||||
secrets: inherit
|
||||
# publish jobs get escalated permissions
|
||||
permissions:
|
||||
"id-token": "write"
|
||||
"packages": "write"
|
||||
|
||||
# Create a GitHub Release while uploading all files to it
|
||||
announce:
|
||||
needs:
|
||||
- plan
|
||||
- host
|
||||
- custom-publish-pypi
|
||||
# use "always() && ..." to allow us to wait for all publish jobs while
|
||||
# still allowing individual publish jobs to skip themselves (for prereleases).
|
||||
# "host" however must run to completion, no skipping allowed!
|
||||
if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') }}
|
||||
runs-on: "ubuntu-20.04"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
# Create a GitHub Release while uploading all files to it
|
||||
- name: "Download GitHub Artifacts"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
- name: Cleanup
|
||||
run: |
|
||||
# Remove the granular manifests
|
||||
rm -f artifacts/*-dist-manifest.json
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
PRERELEASE_FLAG: "${{ fromJson(needs.host.outputs.val).announcement_is_prerelease && '--prerelease' || '' }}"
|
||||
ANNOUNCEMENT_TITLE: "${{ fromJson(needs.host.outputs.val).announcement_title }}"
|
||||
ANNOUNCEMENT_BODY: "${{ fromJson(needs.host.outputs.val).announcement_github_body }}"
|
||||
RELEASE_COMMIT: "${{ github.sha }}"
|
||||
run: |
|
||||
# Write and read notes from a file to avoid quoting breaking things
|
||||
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
|
||||
|
||||
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
|
||||
|
||||
custom-notify-dependents:
|
||||
needs:
|
||||
- plan
|
||||
- announce
|
||||
uses: ./.github/workflows/notify-dependents.yml
|
||||
with:
|
||||
plan: ${{ needs.plan.outputs.val }}
|
||||
secrets: inherit
|
||||
|
||||
custom-publish-docs:
|
||||
needs:
|
||||
- plan
|
||||
- announce
|
||||
uses: ./.github/workflows/publish-docs.yml
|
||||
with:
|
||||
plan: ${{ needs.plan.outputs.val }}
|
||||
secrets: inherit
|
||||
|
||||
custom-publish-playground:
|
||||
needs:
|
||||
- plan
|
||||
- announce
|
||||
uses: ./.github/workflows/publish-playground.yml
|
||||
with:
|
||||
plan: ${{ needs.plan.outputs.val }}
|
||||
secrets: inherit
|
||||
80
.github/workflows/sync_typeshed.yaml
vendored
80
.github/workflows/sync_typeshed.yaml
vendored
@@ -1,80 +0,0 @@
|
||||
name: Sync typeshed
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Run on the 1st and the 15th of every month:
|
||||
- cron: "0 0 1,15 * *"
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync typeshed
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
# Don't run the cron job on forks:
|
||||
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
name: Checkout Ruff
|
||||
with:
|
||||
path: ruff
|
||||
- uses: actions/checkout@v4
|
||||
name: Checkout typeshed
|
||||
with:
|
||||
repository: python/typeshed
|
||||
path: typeshed
|
||||
- name: Setup git
|
||||
run: |
|
||||
git config --global user.name typeshedbot
|
||||
git config --global user.email '<>'
|
||||
- name: Sync typeshed
|
||||
id: sync
|
||||
run: |
|
||||
rm -rf ruff/crates/red_knot_module_resolver/vendor/typeshed
|
||||
mkdir ruff/crates/red_knot_module_resolver/vendor/typeshed
|
||||
cp typeshed/README.md ruff/crates/red_knot_module_resolver/vendor/typeshed
|
||||
cp typeshed/LICENSE ruff/crates/red_knot_module_resolver/vendor/typeshed
|
||||
cp -r typeshed/stdlib ruff/crates/red_knot_module_resolver/vendor/typeshed/stdlib
|
||||
rm -rf ruff/crates/red_knot_module_resolver/vendor/typeshed/stdlib/@tests
|
||||
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_module_resolver/vendor/typeshed/source_commit.txt
|
||||
- name: Commit the changes
|
||||
id: commit
|
||||
if: ${{ steps.sync.outcome == 'success' }}
|
||||
run: |
|
||||
cd ruff
|
||||
git checkout -b typeshedbot/sync-typeshed
|
||||
git add .
|
||||
git diff --staged --quiet || git commit -m "Sync typeshed. Source commit: https://github.com/python/typeshed/commit/$(git -C ../typeshed rev-parse HEAD)"
|
||||
- name: Create a PR
|
||||
if: ${{ steps.sync.outcome == 'success' && steps.commit.outcome == 'success' }}
|
||||
run: |
|
||||
cd ruff
|
||||
git push --force origin typeshedbot/sync-typeshed
|
||||
gh pr list --repo $GITHUB_REPOSITORY --head typeshedbot/sync-typeshed --json id --jq length | grep 1 && exit 0 # exit if there is existing pr
|
||||
gh pr create --title "Sync vendored typeshed stubs" --body "Close and reopen this PR to trigger CI" --label "internal"
|
||||
|
||||
create-issue-on-failure:
|
||||
name: Create an issue if the typeshed sync failed
|
||||
runs-on: ubuntu-latest
|
||||
needs: [sync]
|
||||
if: ${{ github.repository == 'astral-sh/ruff' && always() && github.event_name == 'schedule' && needs.sync.result == 'failure' }}
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.create({
|
||||
owner: "astral-sh",
|
||||
repo: "ruff",
|
||||
title: `Automated typeshed sync failed on ${new Date().toDateString()}`,
|
||||
body: "Runs are listed here: https://github.com/astral-sh/ruff/actions/workflows/sync_typeshed.yaml",
|
||||
})
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -92,7 +92,6 @@ coverage.xml
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
repos/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
@@ -17,4 +17,4 @@ MD013: false
|
||||
# MD024/no-duplicate-heading
|
||||
MD024:
|
||||
# Allow when nested under different parents e.g. CHANGELOG.md
|
||||
siblings_only: true
|
||||
allow_different_nesting: true
|
||||
|
||||
@@ -2,10 +2,9 @@ fail_fast: true
|
||||
|
||||
exclude: |
|
||||
(?x)^(
|
||||
crates/red_knot_module_resolver/vendor/.*|
|
||||
crates/ruff_linter/resources/.*|
|
||||
crates/ruff_linter/src/rules/.*/snapshots/.*|
|
||||
crates/ruff/resources/.*|
|
||||
crates/ruff_cli/resources/.*|
|
||||
crates/ruff_python_formatter/resources/.*|
|
||||
crates/ruff_python_formatter/tests/snapshots/.*|
|
||||
crates/ruff_python_resolver/resources/.*|
|
||||
@@ -14,7 +13,7 @@ exclude: |
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.18
|
||||
rev: v0.15
|
||||
hooks:
|
||||
- id: validate-pyproject
|
||||
|
||||
@@ -32,7 +31,7 @@ repos:
|
||||
)$
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.41.0
|
||||
rev: v0.37.0
|
||||
hooks:
|
||||
- id: markdownlint-fix
|
||||
exclude: |
|
||||
@@ -42,7 +41,7 @@ repos:
|
||||
)$
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.23.1
|
||||
rev: v1.16.22
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
@@ -56,7 +55,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.5.1
|
||||
rev: v0.1.4
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
@@ -71,7 +70,7 @@ repos:
|
||||
|
||||
# Prettier
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.1.0
|
||||
rev: v3.0.3
|
||||
hooks:
|
||||
- id: prettier
|
||||
types: [yaml]
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Auto-generated by `cargo-dist`.
|
||||
.github/workflows/release.yml
|
||||
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"rust-analyzer.check.extraArgs": [
|
||||
"--all-features"
|
||||
],
|
||||
"rust-analyzer.check.command": "clippy",
|
||||
}
|
||||
@@ -1,63 +1,5 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.5.0
|
||||
|
||||
- Follow the XDG specification to discover user-level configurations on macOS (same as on other Unix platforms)
|
||||
- Selecting `ALL` now excludes deprecated rules
|
||||
- The released archives now include an extra level of nesting, which can be removed with `--strip-components=1` when untarring.
|
||||
- The release artifact's file name no longer includes the version tag. This enables users to install via `/latest` URLs on GitHub.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Ruff 2024.2 style
|
||||
|
||||
The formatter now formats code according to the Ruff 2024.2 style guide. Read the [changelog](./CHANGELOG.md#030) for a detailed list of stabilized style changes.
|
||||
|
||||
### `isort`: Use one blank line after imports in typing stub files ([#9971](https://github.com/astral-sh/ruff/pull/9971))
|
||||
|
||||
Previously, Ruff used one or two blank lines (or the number configured by `isort.lines-after-imports`) after imports in typing stub files (`.pyi` files).
|
||||
The [typing style guide for stubs](https://typing.readthedocs.io/en/latest/source/stubs.html#style-guide) recommends using at most 1 blank line for grouping.
|
||||
As of this release, `isort` now always uses one blank line after imports in stub files, the same as the formatter.
|
||||
|
||||
### `build` is no longer excluded by default ([#10093](https://github.com/astral-sh/ruff/pull/10093))
|
||||
|
||||
Ruff maintains a list of directories and files that are excluded by default. This list now consists of the following patterns:
|
||||
|
||||
- `.bzr`
|
||||
- `.direnv`
|
||||
- `.eggs`
|
||||
- `.git`
|
||||
- `.git-rewrite`
|
||||
- `.hg`
|
||||
- `.ipynb_checkpoints`
|
||||
- `.mypy_cache`
|
||||
- `.nox`
|
||||
- `.pants.d`
|
||||
- `.pyenv`
|
||||
- `.pytest_cache`
|
||||
- `.pytype`
|
||||
- `.ruff_cache`
|
||||
- `.svn`
|
||||
- `.tox`
|
||||
- `.venv`
|
||||
- `.vscode`
|
||||
- `__pypackages__`
|
||||
- `_build`
|
||||
- `buck-out`
|
||||
- `dist`
|
||||
- `node_modules`
|
||||
- `site-packages`
|
||||
- `venv`
|
||||
|
||||
Previously, the `build` directory was included in this list. However, the `build` directory tends to be a not-unpopular directory
|
||||
name, and excluding it by default caused confusion. Ruff now no longer excludes `build` except if it is excluded by a `.gitignore` file
|
||||
or because it is listed in `extend-exclude`.
|
||||
|
||||
### `--format` is no longer a valid `rule` or `linter` command option
|
||||
|
||||
Previously, `ruff rule` and `ruff linter` accepted the `--format <FORMAT>` option as an alias for `--output-format`. Ruff no longer
|
||||
supports this alias. Please use `ruff rule --output-format <FORMAT>` and `ruff linter --output-format <FORMAT>` instead.
|
||||
|
||||
## 0.1.9
|
||||
|
||||
### `site-packages` is now excluded by default ([#5513](https://github.com/astral-sh/ruff/pull/5513))
|
||||
|
||||
1372
CHANGELOG.md
1372
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
127
CONTRIBUTING.md
127
CONTRIBUTING.md
@@ -26,25 +26,30 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
|
||||
- [`cargo dev`](#cargo-dev)
|
||||
- [Subsystems](#subsystems)
|
||||
- [Compilation Pipeline](#compilation-pipeline)
|
||||
- [Import Categorization](#import-categorization)
|
||||
- [Project root](#project-root)
|
||||
- [Package root](#package-root)
|
||||
- [Import categorization](#import-categorization-1)
|
||||
|
||||
## The Basics
|
||||
|
||||
Ruff welcomes contributions in the form of pull requests.
|
||||
Ruff welcomes contributions in the form of Pull Requests.
|
||||
|
||||
For small changes (e.g., bug fixes), feel free to submit a PR.
|
||||
|
||||
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
|
||||
creating an [**issue**](https://github.com/astral-sh/ruff/issues) outlining your proposed change.
|
||||
You can also join us on [Discord](https://discord.com/invite/astral-sh) to discuss your idea with the
|
||||
You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5) to discuss your idea with the
|
||||
community. We've labeled [beginner-friendly tasks](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
|
||||
in the issue tracker, along with [bugs](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
||||
and [improvements](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Aaccepted)
|
||||
that are ready for contributions.
|
||||
|
||||
If you're looking for a place to start, we recommend implementing a new lint rule (see:
|
||||
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
|
||||
pattern-match against the examples in the existing codebase. Many lint rules are inspired by
|
||||
existing Python plugins, which can be used as a reference implementation.
|
||||
|
||||
As a concrete example: consider taking on one of the rules from the [`flake8-pyi`](https://github.com/astral-sh/ruff/issues/848)
|
||||
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi) for
|
||||
guidance.
|
||||
|
||||
If you have suggestions on how we might improve the contributing documentation, [let us know](https://github.com/astral-sh/ruff/discussions/5693)!
|
||||
|
||||
### Prerequisites
|
||||
@@ -58,7 +63,7 @@ You'll also need [Insta](https://insta.rs/docs/) to update snapshot tests:
|
||||
cargo install cargo-insta
|
||||
```
|
||||
|
||||
And you'll need pre-commit to run some validation checks:
|
||||
and pre-commit to run some validation checks:
|
||||
|
||||
```shell
|
||||
pipx install pre-commit # or `pip install pre-commit` if you have a virtualenv
|
||||
@@ -71,22 +76,12 @@ when making a commit:
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
We recommend [nextest](https://nexte.st/) to run Ruff's test suite (via `cargo nextest run`),
|
||||
though it's not strictly necessary:
|
||||
|
||||
```shell
|
||||
cargo install cargo-nextest --locked
|
||||
```
|
||||
|
||||
Throughout this guide, any usages of `cargo test` can be replaced with `cargo nextest run`,
|
||||
if you choose to install `nextest`.
|
||||
|
||||
### Development
|
||||
|
||||
After cloning the repository, run Ruff locally from the repository root with:
|
||||
|
||||
```shell
|
||||
cargo run -p ruff -- check /path/to/file.py --no-cache
|
||||
cargo run -p ruff_cli -- check /path/to/file.py --no-cache
|
||||
```
|
||||
|
||||
Prior to opening a pull request, ensure that your code has been auto-formatted,
|
||||
@@ -98,11 +93,9 @@ RUFF_UPDATE_SCHEMA=1 cargo test # Rust testing and updating ruff.schema.json
|
||||
pre-commit run --all-files --show-diff-on-failure # Rust and Python formatting, Markdown and Python linting, etc.
|
||||
```
|
||||
|
||||
These checks will run on GitHub Actions when you open your pull request, but running them locally
|
||||
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
|
||||
will save you time and expedite the merge process.
|
||||
|
||||
If you're using VS Code, you can also install the recommended [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) extension to get these checks while editing.
|
||||
|
||||
Note that many code changes also require updating the snapshot tests, which is done interactively
|
||||
after running `cargo test` like so:
|
||||
|
||||
@@ -110,14 +103,7 @@ after running `cargo test` like so:
|
||||
cargo insta review
|
||||
```
|
||||
|
||||
If your pull request relates to a specific lint rule, include the category and rule code in the
|
||||
title, as in the following examples:
|
||||
|
||||
- \[`flake8-bugbear`\] Avoid false positive for usage after `continue` (`B031`)
|
||||
- \[`flake8-simplify`\] Detect implicit `else` cases in `needless-bool` (`SIM103`)
|
||||
- \[`pycodestyle`\] Implement `redundant-backslash` (`E502`)
|
||||
|
||||
Your pull request will be reviewed by a maintainer, which may involve a few rounds of iteration
|
||||
Your Pull Request will be reviewed by a maintainer, which may involve a few rounds of iteration
|
||||
prior to merging.
|
||||
|
||||
### Project Structure
|
||||
@@ -125,8 +111,8 @@ prior to merging.
|
||||
Ruff is structured as a monorepo with a [flat crate structure](https://matklad.github.io/2021/08/22/large-rust-workspaces.html),
|
||||
such that all crates are contained in a flat `crates` directory.
|
||||
|
||||
The vast majority of the code, including all lint rules, lives in the `ruff_linter` crate (located
|
||||
at `crates/ruff_linter`). As a contributor, that's the crate that'll be most relevant to you.
|
||||
The vast majority of the code, including all lint rules, lives in the `ruff` crate (located at
|
||||
`crates/ruff_linter`). As a contributor, that's the crate that'll be most relevant to you.
|
||||
|
||||
At the time of writing, the repository includes the following crates:
|
||||
|
||||
@@ -134,7 +120,7 @@ At the time of writing, the repository includes the following crates:
|
||||
If you're working on a rule, this is the crate for you.
|
||||
- `crates/ruff_benchmark`: binary crate for running micro-benchmarks.
|
||||
- `crates/ruff_cache`: library crate for caching lint results.
|
||||
- `crates/ruff`: binary crate containing Ruff's command-line interface.
|
||||
- `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
|
||||
- `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g.,
|
||||
`cargo dev generate-all`), see the [`cargo dev`](#cargo-dev) section below.
|
||||
- `crates/ruff_diagnostics`: library crate for the rule-independent abstractions in the lint
|
||||
@@ -199,14 +185,11 @@ and calling out to lint rule analyzer functions as it goes.
|
||||
If you need to inspect the AST, you can run `cargo dev print-ast` with a Python file. Grep
|
||||
for the `Diagnostic::new` invocations to understand how other, similar rules are implemented.
|
||||
|
||||
Once you're satisfied with your code, add tests for your rule
|
||||
(see: [rule testing](#rule-testing-fixtures-and-snapshots)), and regenerate the documentation and
|
||||
associated assets (like our JSON Schema) with `cargo dev generate-all`.
|
||||
Once you're satisfied with your code, add tests for your rule. See [rule testing](#rule-testing-fixtures-and-snapshots)
|
||||
for more details.
|
||||
|
||||
Finally, submit a pull request, and include the category, rule name, and rule code in the title, as
|
||||
in:
|
||||
|
||||
> \[`pycodestyle`\] Implement `redundant-backslash` (`E502`)
|
||||
Finally, regenerate the documentation and other generated assets (like our JSON Schema) with:
|
||||
`cargo dev generate-all`.
|
||||
|
||||
#### Rule naming convention
|
||||
|
||||
@@ -248,7 +231,7 @@ Once you've completed the code for the rule itself, you can define tests with th
|
||||
For example, if you're adding a new rule named `E402`, you would run:
|
||||
|
||||
```shell
|
||||
cargo run -p ruff -- check crates/ruff_linter/resources/test/fixtures/pycodestyle/E402.py --no-cache --preview --select E402
|
||||
cargo run -p ruff_cli -- check crates/ruff_linter/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402
|
||||
```
|
||||
|
||||
**Note:** Only a subset of rules are enabled by default. When testing a new rule, ensure that
|
||||
@@ -269,7 +252,7 @@ Once you've completed the code for the rule itself, you can define tests with th
|
||||
|
||||
Ruff's user-facing settings live in a few different places.
|
||||
|
||||
First, the command-line options are defined via the `Args` struct in `crates/ruff/src/args.rs`.
|
||||
First, the command-line options are defined via the `Args` struct in `crates/ruff_cli/src/args.rs`.
|
||||
|
||||
Second, the `pyproject.toml` options are defined in `crates/ruff_workspace/src/options.rs` (via the
|
||||
`Options` struct), `crates/ruff_workspace/src/configuration.rs` (via the `Configuration` struct),
|
||||
@@ -280,7 +263,7 @@ These represent, respectively: the schema used to parse the `pyproject.toml` fil
|
||||
intermediate representation; and the final, internal representation used to power Ruff.
|
||||
|
||||
To add a new configuration option, you'll likely want to modify these latter few files (along with
|
||||
`args.rs`, if appropriate). If you want to pattern-match against an existing example, grep for
|
||||
`arg.rs`, if appropriate). If you want to pattern-match against an existing example, grep for
|
||||
`dummy_variable_rgx`, which defines a regular expression to match against acceptable unused
|
||||
variables (e.g., `_`).
|
||||
|
||||
@@ -319,7 +302,7 @@ To preview any changes to the documentation locally:
|
||||
```
|
||||
|
||||
The documentation should then be available locally at
|
||||
[http://127.0.0.1:8000/ruff/](http://127.0.0.1:8000/ruff/).
|
||||
[http://127.0.0.1:8000/docs/](http://127.0.0.1:8000/docs/).
|
||||
|
||||
## Release Process
|
||||
|
||||
@@ -332,13 +315,13 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
|
||||
|
||||
### Creating a new release
|
||||
|
||||
1. Install `uv`: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
||||
1. Run `./scripts/release.sh`; this command will:
|
||||
- Generate a temporary virtual environment with `rooster`
|
||||
We use an experimental in-house tool for managing releases.
|
||||
|
||||
1. Install `rooster`: `pip install git+https://github.com/zanieb/rooster@main`
|
||||
1. Run `rooster release`; this command will:
|
||||
- Generate a changelog entry in `CHANGELOG.md`
|
||||
- Update versions in `pyproject.toml` and `Cargo.toml`
|
||||
- Update references to versions in the `README.md` and documentation
|
||||
- Display contributors for the release
|
||||
1. The changelog should then be editorialized for consistency
|
||||
- Often labels will be missing from pull requests they will need to be manually organized into the proper section
|
||||
- Changes should be edited to be user-facing descriptions, avoiding internal details
|
||||
@@ -346,22 +329,23 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
|
||||
1. Run `cargo check`. This should update the lock file with new versions.
|
||||
1. Create a pull request with the changelog and version updates
|
||||
1. Merge the PR
|
||||
1. Run the [release workflow](https://github.com/astral-sh/ruff/actions/workflows/release.yml) with:
|
||||
1. Run the [release workflow](https://github.com/astral-sh/ruff/actions/workflows/release.yaml) with:
|
||||
- The new version number (without starting `v`)
|
||||
- The commit hash of the merged release pull request on `main`
|
||||
1. The release workflow will do the following:
|
||||
1. Build all the assets. If this fails (even though we tested in step 4), we haven't tagged or
|
||||
uploaded anything, you can restart after pushing a fix. If you just need to rerun the build,
|
||||
make sure you're [re-running all the failed
|
||||
jobs](https://docs.github.com/en/actions/managing-workflow-runs/re-running-workflows-and-jobs#re-running-failed-jobs-in-a-workflow) and not just a single failed job.
|
||||
uploaded anything, you can restart after pushing a fix.
|
||||
1. Upload to PyPI.
|
||||
1. Create and push the Git tag (as extracted from `pyproject.toml`). We create the Git tag only
|
||||
after building the wheels and uploading to PyPI, since we can't delete or modify the tag ([#4468](https://github.com/astral-sh/ruff/issues/4468)).
|
||||
1. Attach artifacts to draft GitHub release
|
||||
1. Trigger downstream repositories. This can fail non-catastrophically, as we can run any
|
||||
downstream jobs manually if needed.
|
||||
1. Verify the GitHub release:
|
||||
1. The Changelog should match the content of `CHANGELOG.md`
|
||||
1. Append the contributors from the `bump.sh` script
|
||||
1. Publish the GitHub release
|
||||
1. Open the draft release in the GitHub release section
|
||||
1. Copy the changelog for the release into the GitHub release
|
||||
- See previous releases for formatting of section headers
|
||||
1. Generate the contributor list with `rooster contributors` and add to the release notes
|
||||
1. If needed, [update the schemastore](https://github.com/astral-sh/ruff/blob/main/scripts/update_schemastore.py).
|
||||
1. One can determine if an update is needed when
|
||||
`git diff old-version-tag new-version-tag -- ruff.schema.json` returns a non-empty diff.
|
||||
@@ -389,11 +373,6 @@ We have several ways of benchmarking and profiling Ruff:
|
||||
- Microbenchmarks which run the linter or the formatter on individual files. These run on pull requests.
|
||||
- Profiling the linter on either the microbenchmarks or entire projects
|
||||
|
||||
> \[!NOTE\]
|
||||
> When running benchmarks, ensure that your CPU is otherwise idle (e.g., close any background
|
||||
> applications, like web browsers). You may also want to switch your CPU to a "performance"
|
||||
> mode, if it exists, especially when benchmarking short-lived processes.
|
||||
|
||||
### CPython Benchmark
|
||||
|
||||
First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase,
|
||||
@@ -539,10 +518,10 @@ if the benchmark improved/regressed compared to that baseline.
|
||||
|
||||
```shell
|
||||
# Run once on your "baseline" code
|
||||
cargo bench -p ruff_benchmark -- --save-baseline=main
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# Then iterate with
|
||||
cargo bench -p ruff_benchmark -- --baseline=main
|
||||
cargo benchmark --baseline=main
|
||||
```
|
||||
|
||||
#### PR Summary
|
||||
@@ -552,10 +531,10 @@ This is useful to illustrate the improvements of a PR.
|
||||
|
||||
```shell
|
||||
# On main
|
||||
cargo bench -p ruff_benchmark -- --save-baseline=main
|
||||
cargo benchmark --save-baseline=main
|
||||
|
||||
# After applying your changes
|
||||
cargo bench -p ruff_benchmark -- --save-baseline=pr
|
||||
cargo benchmark --save-baseline=pr
|
||||
|
||||
critcmp main pr
|
||||
```
|
||||
@@ -568,10 +547,10 @@ cargo install critcmp
|
||||
|
||||
#### Tips
|
||||
|
||||
- Use `cargo bench -p ruff_benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark lexer`
|
||||
to only run the lexer benchmarks.
|
||||
- Use `cargo bench -p ruff_benchmark -- --quiet` for a more cleaned up output (without statistical relevance)
|
||||
- Use `cargo bench -p ruff_benchmark -- --quick` to get faster results (more prone to noise)
|
||||
- Use `cargo benchmark <filter>` to only run specific benchmarks. For example: `cargo benchmark linter/pydantic`
|
||||
to only run the pydantic tests.
|
||||
- Use `cargo benchmark --quiet` for a more cleaned up output (without statistical relevance)
|
||||
- Use `cargo benchmark --quick` to get faster results (more prone to noise)
|
||||
|
||||
### Profiling Projects
|
||||
|
||||
@@ -638,11 +617,11 @@ Otherwise, follow the instructions from the linux section.
|
||||
`cargo dev` is a shortcut for `cargo run --package ruff_dev --bin ruff_dev`. You can run some useful
|
||||
utils with it:
|
||||
|
||||
- `cargo dev print-ast <file>`: Print the AST of a python file using Ruff's
|
||||
[Python parser](https://github.com/astral-sh/ruff/tree/main/crates/ruff_python_parser).
|
||||
For `if True: pass # comment`, you can see the syntax tree, the byte offsets for start and
|
||||
stop of each node and also how the `:` token, the comment and whitespace are not represented
|
||||
anymore:
|
||||
- `cargo dev print-ast <file>`: Print the AST of a python file using the
|
||||
[RustPython parser](https://github.com/astral-sh/RustPython-Parser/tree/main/parser) that is
|
||||
mainly used in Ruff. For `if True: pass # comment`, you can see the syntax tree, the byte offsets
|
||||
for start and stop of each node and also how the `:` token, the comment and whitespace are not
|
||||
represented anymore:
|
||||
|
||||
```text
|
||||
[
|
||||
@@ -815,8 +794,8 @@ To understand Ruff's import categorization system, we first need to define two c
|
||||
"project root".)
|
||||
- "Package root": The top-most directory defining the Python package that includes a given Python
|
||||
file. To find the package root for a given Python file, traverse up its parent directories until
|
||||
you reach a parent directory that doesn't contain an `__init__.py` file (and isn't in a subtree
|
||||
marked as a [namespace package](https://docs.astral.sh/ruff/settings/#namespace-packages)); take the directory
|
||||
you reach a parent directory that doesn't contain an `__init__.py` file (and isn't marked as
|
||||
a [namespace package](https://docs.astral.sh/ruff/settings/#namespace-packages)); take the directory
|
||||
just before that, i.e., the first directory in the package.
|
||||
|
||||
For example, given:
|
||||
|
||||
2019
Cargo.lock
generated
2019
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
220
Cargo.toml
220
Cargo.toml
@@ -4,7 +4,7 @@ resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.75"
|
||||
rust-version = "1.71"
|
||||
homepage = "https://docs.astral.sh/ruff"
|
||||
documentation = "https://docs.astral.sh/ruff"
|
||||
repository = "https://github.com/astral-sh/ruff"
|
||||
@@ -12,147 +12,103 @@ authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
ruff = { path = "crates/ruff" }
|
||||
ruff_cache = { path = "crates/ruff_cache" }
|
||||
ruff_db = { path = "crates/ruff_db" }
|
||||
ruff_diagnostics = { path = "crates/ruff_diagnostics" }
|
||||
ruff_formatter = { path = "crates/ruff_formatter" }
|
||||
ruff_index = { path = "crates/ruff_index" }
|
||||
ruff_linter = { path = "crates/ruff_linter" }
|
||||
ruff_macros = { path = "crates/ruff_macros" }
|
||||
ruff_notebook = { path = "crates/ruff_notebook" }
|
||||
ruff_python_ast = { path = "crates/ruff_python_ast" }
|
||||
ruff_python_codegen = { path = "crates/ruff_python_codegen" }
|
||||
ruff_python_formatter = { path = "crates/ruff_python_formatter" }
|
||||
ruff_python_index = { path = "crates/ruff_python_index" }
|
||||
ruff_python_literal = { path = "crates/ruff_python_literal" }
|
||||
ruff_python_parser = { path = "crates/ruff_python_parser" }
|
||||
ruff_python_semantic = { path = "crates/ruff_python_semantic" }
|
||||
ruff_python_stdlib = { path = "crates/ruff_python_stdlib" }
|
||||
ruff_python_trivia = { path = "crates/ruff_python_trivia" }
|
||||
ruff_server = { path = "crates/ruff_server" }
|
||||
ruff_source_file = { path = "crates/ruff_source_file" }
|
||||
ruff_text_size = { path = "crates/ruff_text_size" }
|
||||
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" }
|
||||
|
||||
aho-corasick = { version = "1.1.3" }
|
||||
aho-corasick = { version = "1.1.2" }
|
||||
annotate-snippets = { version = "0.9.2", features = ["color"] }
|
||||
anyhow = { version = "1.0.80" }
|
||||
argfile = { version = "0.2.0" }
|
||||
anyhow = { version = "1.0.79" }
|
||||
argfile = { version = "0.1.6" }
|
||||
assert_cmd = { version = "2.0.8" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bitflags = { version = "2.5.0" }
|
||||
bstr = { version = "1.9.1" }
|
||||
bitflags = { version = "2.4.1" }
|
||||
cachedir = { version = "0.3.1" }
|
||||
camino = { version = "1.1.7" }
|
||||
chrono = { version = "0.4.35", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.5.3", features = ["derive"] }
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.4.13", features = ["derive"] }
|
||||
clap_complete_command = { version = "0.5.1" }
|
||||
clearscreen = { version = "3.0.0" }
|
||||
codspeed-criterion-compat = { version = "2.6.0", default-features = false }
|
||||
clearscreen = { version = "2.0.0" }
|
||||
codspeed-criterion-compat = { version = "2.3.3", default-features = false }
|
||||
colored = { version = "2.1.0" }
|
||||
configparser = { version = "3.0.3" }
|
||||
console_error_panic_hook = { version = "0.1.7" }
|
||||
console_log = { version = "1.0.0" }
|
||||
countme = { version = "3.0.1" }
|
||||
compact_str = "0.7.1"
|
||||
countme = { version ="3.0.1"}
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
crossbeam = { version = "0.8.4" }
|
||||
dashmap = { version = "6.0.1" }
|
||||
dirs = { version = "5.0.0" }
|
||||
drop_bomb = { version = "0.1.5" }
|
||||
env_logger = { version = "0.11.0" }
|
||||
etcetera = { version = "0.8.0" }
|
||||
env_logger = { version ="0.10.1"}
|
||||
fern = { version = "0.6.1" }
|
||||
filetime = { version = "0.2.23" }
|
||||
fs-err = { version ="2.11.0"}
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.14" }
|
||||
hashbrown = "0.14.3"
|
||||
ignore = { version = "0.4.22" }
|
||||
imara-diff = { version = "0.1.5" }
|
||||
hexf-parse = { version ="0.2.1"}
|
||||
ignore = { version = "0.4.21" }
|
||||
imara-diff ={ version = "0.1.5"}
|
||||
imperative = { version = "1.0.4" }
|
||||
indicatif = { version = "0.17.8" }
|
||||
indoc = { version = "2.0.4" }
|
||||
insta = { version = "1.35.1" }
|
||||
insta-cmd = { version = "0.6.0" }
|
||||
is-macro = { version = "0.3.5" }
|
||||
indicatif ={ version = "0.17.7"}
|
||||
indoc ={ version = "2.0.4"}
|
||||
insta = { version = "1.34.0", feature = ["filters", "glob"] }
|
||||
insta-cmd = { version = "0.4.0" }
|
||||
is-macro = { version = "0.3.4" }
|
||||
is-wsl = { version = "0.4.0" }
|
||||
itertools = { version = "0.13.0" }
|
||||
js-sys = { version = "0.3.69" }
|
||||
jod-thread = { version = "0.1.2" }
|
||||
libc = { version = "0.2.153" }
|
||||
itertools = { version = "0.12.0" }
|
||||
js-sys = { version = "0.3.66" }
|
||||
lalrpop-util = { version = "0.20.0", default-features = false }
|
||||
lexical-parse-float = { version = "0.8.0", features = ["format"] }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
log = { version = "0.4.17" }
|
||||
lsp-server = { version = "0.7.6" }
|
||||
lsp-types = { git = "https://github.com/astral-sh/lsp-types.git", rev = "3512a9f", features = [
|
||||
"proposed",
|
||||
] }
|
||||
matchit = { version = "0.8.1" }
|
||||
memchr = { version = "2.7.1" }
|
||||
mimalloc = { version = "0.1.39" }
|
||||
memchr = { version = "2.6.4" }
|
||||
mimalloc = { version ="0.1.39"}
|
||||
natord = { version = "1.0.9" }
|
||||
notify = { version = "6.1.1" }
|
||||
once_cell = { version = "1.19.0" }
|
||||
ordermap = { version = "0.5.0" }
|
||||
path-absolutize = { version = "3.1.1" }
|
||||
path-slash = { version = "0.2.1" }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.6.0", features = ["serde"] }
|
||||
pep440_rs = { version = "0.4.0", features = ["serde"] }
|
||||
pretty_assertions = "1.3.0"
|
||||
proc-macro2 = { version = "1.0.79" }
|
||||
pyproject-toml = { version = "0.9.0" }
|
||||
quick-junit = { version = "0.4.0" }
|
||||
proc-macro2 = { version = "1.0.73" }
|
||||
pyproject-toml = { version = "0.8.1" }
|
||||
quick-junit = { version = "0.3.5" }
|
||||
quote = { version = "1.0.23" }
|
||||
rand = { version = "0.8.5" }
|
||||
rayon = { version = "1.10.0" }
|
||||
rayon = { version = "1.8.0" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a1bf3a613f451af7fc0a59411c56abc47fe8e8e1" }
|
||||
result-like = { version = "0.5.0" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde-wasm-bindgen = { version = "0.6.4" }
|
||||
serde_json = { version = "1.0.113" }
|
||||
seahash = { version ="4.1.0"}
|
||||
semver = { version = "1.0.20" }
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde-wasm-bindgen = { version = "0.6.3" }
|
||||
serde_json = { version = "1.0.109" }
|
||||
serde_test = { version = "1.0.152" }
|
||||
serde_with = { version = "3.6.0", default-features = false, features = [
|
||||
"macros",
|
||||
] }
|
||||
serde_with = { version = "3.4.0", default-features = false, features = ["macros"] }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.4.0", features = ["inline"] }
|
||||
smallvec = { version = "1.13.2" }
|
||||
shlex = { version ="1.2.0"}
|
||||
similar = { version = "2.3.0", features = ["inline"] }
|
||||
smallvec = { version = "1.11.2" }
|
||||
static_assertions = "1.1.0"
|
||||
strum = { version = "0.26.0", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.26.0" }
|
||||
syn = { version = "2.0.55" }
|
||||
tempfile = { version = "3.9.0" }
|
||||
strum = { version = "0.25.0", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.25.3" }
|
||||
syn = { version = "2.0.40" }
|
||||
tempfile = { version ="3.9.0"}
|
||||
test-case = { version = "3.3.1" }
|
||||
thiserror = { version = "1.0.58" }
|
||||
tikv-jemallocator = { version = "0.5.0" }
|
||||
toml = { version = "0.8.11" }
|
||||
thiserror = { version = "1.0.51" }
|
||||
tikv-jemallocator = { version ="0.5.0"}
|
||||
toml = { version = "0.8.8" }
|
||||
tracing = { version = "0.1.40" }
|
||||
tracing-indicatif = { version = "0.3.6" }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
tracing-tree = { version = "0.3.0" }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unic-ucd-category = { version = "0.9" }
|
||||
unic-ucd-category = { version ="0.9"}
|
||||
unicode-ident = { version = "1.0.12" }
|
||||
unicode-width = { version = "0.1.11" }
|
||||
unicode_names2 = { version = "1.2.2" }
|
||||
unicode-normalization = { version = "0.1.23" }
|
||||
ureq = { version = "2.9.6" }
|
||||
unicode_names2 = { version = "1.2.1" }
|
||||
ureq = { version = "2.9.1" }
|
||||
url = { version = "2.5.0" }
|
||||
uuid = { version = "1.6.1", features = [
|
||||
"v4",
|
||||
"fast-rng",
|
||||
"macro-diagnostics",
|
||||
"js",
|
||||
] }
|
||||
uuid = { version = "1.6.1", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
walkdir = { version = "2.3.2" }
|
||||
wasm-bindgen = { version = "0.2.92" }
|
||||
wasm-bindgen-test = { version = "0.3.42" }
|
||||
wasm-bindgen = { version = "0.2.84" }
|
||||
wasm-bindgen-test = { version = "0.3.39" }
|
||||
wild = { version = "2" }
|
||||
zip = { version = "0.6.6", default-features = false, features = ["zstd"] }
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "warn"
|
||||
@@ -165,7 +121,6 @@ char_lit_as_u8 = "allow"
|
||||
collapsible_else_if = "allow"
|
||||
collapsible_if = "allow"
|
||||
implicit_hasher = "allow"
|
||||
map_unwrap_or = "allow"
|
||||
match_same_arms = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
@@ -220,62 +175,3 @@ opt-level = 1
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
debug = 1
|
||||
|
||||
# The profile that 'cargo dist' will build with.
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
|
||||
# Config for 'cargo dist'
|
||||
[workspace.metadata.dist]
|
||||
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.18.0"
|
||||
# CI backends to support
|
||||
ci = ["github"]
|
||||
# The installers to generate for each app
|
||||
installers = ["shell", "powershell"]
|
||||
# The archive format to use for windows builds (defaults .zip)
|
||||
windows-archive = ".zip"
|
||||
# The archive format to use for non-windows builds (defaults .tar.xz)
|
||||
unix-archive = ".tar.gz"
|
||||
# Target platforms to build apps for (Rust target-triple syntax)
|
||||
targets = [
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"arm-unknown-linux-musleabihf",
|
||||
"armv7-unknown-linux-gnueabihf",
|
||||
"armv7-unknown-linux-musleabihf",
|
||||
"i686-pc-windows-msvc",
|
||||
"i686-unknown-linux-gnu",
|
||||
"i686-unknown-linux-musl",
|
||||
"powerpc64-unknown-linux-gnu",
|
||||
"powerpc64le-unknown-linux-gnu",
|
||||
"s390x-unknown-linux-gnu",
|
||||
"x86_64-apple-darwin",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-musl",
|
||||
]
|
||||
# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true)
|
||||
auto-includes = false
|
||||
# Whether cargo-dist should create a GitHub Release or use an existing draft
|
||||
create-release = true
|
||||
# Publish jobs to run in CI
|
||||
pr-run-mode = "skip"
|
||||
# Whether CI should trigger releases with dispatches instead of tag pushes
|
||||
dispatch-releases = true
|
||||
# The stage during which the GitHub Release should be created
|
||||
github-release = "announce"
|
||||
# Whether CI should include auto-generated code to build local artifacts
|
||||
build-local-artifacts = false
|
||||
# Local artifacts jobs to run in CI
|
||||
local-artifacts-jobs = ["./build-binaries", "./build-docker"]
|
||||
# Publish jobs to run in CI
|
||||
publish-jobs = ["./publish-pypi"]
|
||||
# Announcement jobs to run in CI
|
||||
post-announce-jobs = ["./notify-dependents", "./publish-docs", "./publish-playground"]
|
||||
# Custom permissions for GitHub Jobs
|
||||
github-custom-job-permissions = { "build-docker" = { packages = "write", contents = "read" } }
|
||||
# Whether to install an updater program
|
||||
install-updater = false
|
||||
|
||||
87
README.md
87
README.md
@@ -4,12 +4,11 @@
|
||||
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://github.com/astral-sh/ruff/blob/main/LICENSE)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://github.com/astral-sh/ruff/actions)
|
||||
[](https://discord.com/invite/astral-sh)
|
||||
|
||||
[**Docs**](https://docs.astral.sh/ruff/) | [**Playground**](https://play.ruff.rs/)
|
||||
[**Discord**](https://discord.gg/c9MhzV8aU5) | [**Docs**](https://docs.astral.sh/ruff/) | [**Playground**](https://play.ruff.rs/)
|
||||
|
||||
An extremely fast Python linter and code formatter, written in Rust.
|
||||
|
||||
@@ -28,11 +27,11 @@ An extremely fast Python linter and code formatter, written in Rust.
|
||||
- ⚡️ 10-100x faster than existing linters (like Flake8) and formatters (like Black)
|
||||
- 🐍 Installable via `pip`
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 🤝 Python 3.13 compatibility
|
||||
- 🤝 Python 3.12 compatibility
|
||||
- ⚖️ Drop-in parity with [Flake8](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8), isort, and Black
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 📏 Over [800 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
|
||||
- 📏 Over [700 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
|
||||
of popular Flake8 plugins, like flake8-bugbear
|
||||
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/integrations/) for
|
||||
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
|
||||
@@ -50,7 +49,6 @@ times faster than any individual tool.
|
||||
Ruff is extremely actively developed and used in major open-source projects like:
|
||||
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- [Apache Superset](https://github.com/apache/superset)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Hugging Face](https://github.com/huggingface/transformers)
|
||||
- [Pandas](https://github.com/pandas-dev/pandas)
|
||||
@@ -119,25 +117,7 @@ For more, see the [documentation](https://docs.astral.sh/ruff/).
|
||||
Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
|
||||
```shell
|
||||
# With pip.
|
||||
pip install ruff
|
||||
|
||||
# With pipx.
|
||||
pipx install ruff
|
||||
```
|
||||
|
||||
Starting with version `0.5.0`, Ruff can be installed with our standalone installers:
|
||||
|
||||
```shell
|
||||
# On macOS and Linux.
|
||||
curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
|
||||
# On Windows.
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.5.2/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.5.2/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -148,7 +128,7 @@ and with [a variety of other package managers](https://docs.astral.sh/ruff/insta
|
||||
To run Ruff as a linter, try any of the following:
|
||||
|
||||
```shell
|
||||
ruff check # Lint all files in the current directory (and any subdirectories).
|
||||
ruff check . # Lint all files in the current directory (and any subdirectories).
|
||||
ruff check path/to/code/ # Lint all files in `/path/to/code` (and any subdirectories).
|
||||
ruff check path/to/code/*.py # Lint all `.py` files in `/path/to/code`.
|
||||
ruff check path/to/code/to/file.py # Lint `file.py`.
|
||||
@@ -158,7 +138,7 @@ ruff check @arguments.txt # Lint using an input file, treating its con
|
||||
Or, to run Ruff as a formatter:
|
||||
|
||||
```shell
|
||||
ruff format # Format all files in the current directory (and any subdirectories).
|
||||
ruff format . # Format all files in the current directory (and any subdirectories).
|
||||
ruff format path/to/code/ # Format all files in `/path/to/code` (and any subdirectories).
|
||||
ruff format path/to/code/*.py # Format all `.py` files in `/path/to/code`.
|
||||
ruff format path/to/code/to/file.py # Format `file.py`.
|
||||
@@ -170,7 +150,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.2
|
||||
rev: v0.1.11
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -192,7 +172,7 @@ jobs:
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: chartboost/ruff-action@v1
|
||||
```
|
||||
|
||||
@@ -202,9 +182,10 @@ Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml`
|
||||
[_Configuration_](https://docs.astral.sh/ruff/configuration/), or [_Settings_](https://docs.astral.sh/ruff/settings/)
|
||||
for a complete list of all configuration options).
|
||||
|
||||
If left unspecified, Ruff's default configuration is equivalent to the following `ruff.toml` file:
|
||||
If left unspecified, Ruff's default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
".bzr",
|
||||
@@ -242,7 +223,7 @@ indent-width = 4
|
||||
# Assume Python 3.8
|
||||
target-version = "py38"
|
||||
|
||||
[lint]
|
||||
[tool.ruff.lint]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
ignore = []
|
||||
@@ -254,7 +235,7 @@ unfixable = []
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
[format]
|
||||
[tool.ruff.format]
|
||||
# Like Black, use double quotes for strings.
|
||||
quote-style = "double"
|
||||
|
||||
@@ -268,27 +249,13 @@ skip-magic-trailing-comma = false
|
||||
line-ending = "auto"
|
||||
```
|
||||
|
||||
Note that, in a `pyproject.toml`, each section header should be prefixed with `tool.ruff`. For
|
||||
example, `[lint]` should be replaced with `[tool.ruff.lint]`.
|
||||
|
||||
Some configuration options can be provided via dedicated command-line arguments, such as those
|
||||
related to rule enablement and disablement, file discovery, and logging level:
|
||||
Some configuration options can be provided via the command-line, such as those related to
|
||||
rule enablement and disablement, file discovery, and logging level:
|
||||
|
||||
```shell
|
||||
ruff check --select F401 --select F403 --quiet
|
||||
ruff check path/to/code/ --select F401 --select F403 --quiet
|
||||
```
|
||||
|
||||
The remaining configuration options can be provided through a catch-all `--config` argument:
|
||||
|
||||
```shell
|
||||
ruff check --config "lint.per-file-ignores = {'some_file.py' = ['F841']}"
|
||||
```
|
||||
|
||||
To opt in to the latest lint rules, formatter style changes, interface updates, and more, enable
|
||||
[preview mode](https://docs.astral.sh/ruff/rules/) by setting `preview = true` in your configuration
|
||||
file or passing `--preview` on the command line. Preview mode enables a collection of unstable
|
||||
features that may change prior to stabilization.
|
||||
|
||||
See `ruff help` for more on Ruff's top-level commands, or `ruff help check` and `ruff help format`
|
||||
for more on the linting and formatting commands, respectively.
|
||||
|
||||
@@ -296,7 +263,7 @@ for more on the linting and formatting commands, respectively.
|
||||
|
||||
<!-- Begin section: Rules -->
|
||||
|
||||
**Ruff supports over 800 lint rules**, many of which are inspired by popular tools like Flake8,
|
||||
**Ruff supports over 700 lint rules**, many of which are inspired by popular tools like Flake8,
|
||||
isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implements every rule in
|
||||
Rust as a first-party feature.
|
||||
|
||||
@@ -352,6 +319,7 @@ quality tools, including:
|
||||
- [flake8-super](https://pypi.org/project/flake8-super/)
|
||||
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [flake8-todos](https://pypi.org/project/flake8-todos/)
|
||||
- [flake8-trio](https://pypi.org/project/flake8-trio/)
|
||||
- [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/astral-sh/ruff/issues/2102))
|
||||
@@ -373,14 +341,14 @@ For a complete enumeration of the supported rules, see [_Rules_](https://docs.as
|
||||
Contributions are welcome and highly appreciated. To get started, check out the
|
||||
[**contributing guidelines**](https://docs.astral.sh/ruff/contributing/).
|
||||
|
||||
You can also join us on [**Discord**](https://discord.com/invite/astral-sh).
|
||||
You can also join us on [**Discord**](https://discord.gg/c9MhzV8aU5).
|
||||
|
||||
## Support
|
||||
|
||||
Having trouble? Check out the existing issues on [**GitHub**](https://github.com/astral-sh/ruff/issues),
|
||||
or feel free to [**open a new one**](https://github.com/astral-sh/ruff/issues/new).
|
||||
|
||||
You can also ask for help on [**Discord**](https://discord.com/invite/astral-sh).
|
||||
You can also ask for help on [**Discord**](https://discord.gg/c9MhzV8aU5).
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
@@ -410,7 +378,6 @@ Ruff is released under the MIT license.
|
||||
|
||||
Ruff is used by a number of major open-source projects and companies, including:
|
||||
|
||||
- [Albumentations](https://github.com/albumentations-team/albumentations)
|
||||
- Amazon ([AWS SAM](https://github.com/aws/serverless-application-model))
|
||||
- Anthropic ([Python SDK](https://github.com/anthropics/anthropic-sdk-python))
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
@@ -425,7 +392,6 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- Databricks ([MLflow](https://github.com/mlflow/mlflow))
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Godot](https://github.com/godotengine/godot)
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- [HTTPX](https://github.com/encode/httpx)
|
||||
@@ -436,9 +402,7 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
[Diffusers](https://github.com/huggingface/diffusers))
|
||||
- ING Bank ([popmon](https://github.com/ing-bank/popmon), [probatus](https://github.com/ing-bank/probatus))
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [ivy](https://github.com/unifyai/ivy)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [Kraken Tech](https://kraken.tech/)
|
||||
- [LangChain](https://github.com/hwchase17/langchain)
|
||||
- [Litestar](https://litestar.dev/)
|
||||
- [LlamaIndex](https://github.com/jerryjliu/llama_index)
|
||||
@@ -448,18 +412,15 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel),
|
||||
[ONNX Runtime](https://github.com/microsoft/onnxruntime),
|
||||
[LightGBM](https://github.com/microsoft/LightGBM))
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python))
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [Mypy](https://github.com/python/mypy)
|
||||
- [Nautobot](https://github.com/nautobot/nautobot)
|
||||
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
||||
- [Neon](https://github.com/neondatabase/neon)
|
||||
- [Nokia](https://nokia.com/)
|
||||
- [NoneBot](https://github.com/nonebot/nonebot2)
|
||||
- [NumPyro](https://github.com/pyro-ppl/numpyro)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
|
||||
- [Open Wine Components](https://github.com/Open-Wine-Components/umu-launcher)
|
||||
- [PDM](https://github.com/pdm-project/pdm)
|
||||
- [PaddlePaddle](https://github.com/PaddlePaddle/Paddle)
|
||||
- [Pandas](https://github.com/pandas-dev/pandas)
|
||||
@@ -471,7 +432,6 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [PyInstaller](https://github.com/pyinstaller/pyinstaller)
|
||||
- [PyMC](https://github.com/pymc-devs/pymc/)
|
||||
- [PyMC-Marketing](https://github.com/pymc-labs/pymc-marketing)
|
||||
- [pytest](https://github.com/pytest-dev/pytest)
|
||||
- [PyTorch](https://github.com/pytorch/pytorch)
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
@@ -487,7 +447,6 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
|
||||
- [Starlette](https://github.com/encode/starlette)
|
||||
- [Streamlit](https://github.com/streamlit/streamlit)
|
||||
- [The Algorithms](https://github.com/TheAlgorithms/Python)
|
||||
- [Vega-Altair](https://github.com/altair-viz/altair)
|
||||
- WordPress ([Openverse](https://github.com/WordPress/openverse))
|
||||
@@ -503,7 +462,7 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
|
||||
### Show Your Support
|
||||
|
||||
If you're using Ruff, consider adding the Ruff badge to your project's `README.md`:
|
||||
If you're using Ruff, consider adding the Ruff badge to project's `README.md`:
|
||||
|
||||
```md
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
@@ -525,10 +484,10 @@ If you're using Ruff, consider adding the Ruff badge to your project's `README.m
|
||||
|
||||
## License
|
||||
|
||||
This repository is licensed under the [MIT License](https://github.com/astral-sh/ruff/blob/main/LICENSE)
|
||||
MIT
|
||||
|
||||
<div align="center">
|
||||
<a target="_blank" href="https://astral.sh" style="background:none">
|
||||
<img src="https://raw.githubusercontent.com/astral-sh/ruff/main/assets/svg/Astral.svg" alt="Made by Astral">
|
||||
<img src="https://raw.githubusercontent.com/astral-sh/ruff/main/assets/svg/Astral.svg">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
12
_typos.toml
12
_typos.toml
@@ -1,21 +1,11 @@
|
||||
[files]
|
||||
# https://github.com/crate-ci/typos/issues/868
|
||||
extend-exclude = ["crates/red_knot_module_resolver/vendor/**/*", "**/resources/**/*", "**/snapshots/**/*"]
|
||||
extend-exclude = ["**/resources/**/*", "**/snapshots/**/*"]
|
||||
|
||||
[default.extend-words]
|
||||
"arange" = "arange" # e.g. `numpy.arange`
|
||||
hel = "hel"
|
||||
whos = "whos"
|
||||
spawnve = "spawnve"
|
||||
ned = "ned"
|
||||
pn = "pn" # `import panel as pd` is a thing
|
||||
poit = "poit"
|
||||
BA = "BA" # acronym for "Bad Allowed", used in testing.
|
||||
jod = "jod" # e.g., `jod-thread`
|
||||
|
||||
[default]
|
||||
extend-ignore-re = [
|
||||
# Line ignore with trailing "spellchecker:disable-line"
|
||||
"(?Rm)^.*#\\s*spellchecker:disable-line$",
|
||||
"LICENSEs",
|
||||
]
|
||||
|
||||
14
clippy.toml
14
clippy.toml
@@ -1,13 +1,7 @@
|
||||
doc-valid-idents = [
|
||||
"..",
|
||||
"CodeQL",
|
||||
"FastAPI",
|
||||
"IPython",
|
||||
"LangChain",
|
||||
"LibCST",
|
||||
"McCabe",
|
||||
"NumPy",
|
||||
"SCREAMING_SNAKE_CASE",
|
||||
"SQLAlchemy",
|
||||
"StackOverflow",
|
||||
"CodeQL",
|
||||
"IPython",
|
||||
"NumPy",
|
||||
"..",
|
||||
]
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
[package]
|
||||
name = "red_knot"
|
||||
version = "0.0.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
red_knot_module_resolver = { workspace = true }
|
||||
red_knot_python_semantic = { workspace = true }
|
||||
|
||||
ruff_db = { workspace = true }
|
||||
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 }
|
||||
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,10 +0,0 @@
|
||||
use red_knot_python_semantic::Db as SemanticDb;
|
||||
use ruff_db::Upcast;
|
||||
use salsa::DbWithJar;
|
||||
|
||||
use crate::lint::{lint_semantic, lint_syntax, unwind_if_cancelled};
|
||||
|
||||
pub trait Db: DbWithJar<Jar> + SemanticDb + Upcast<dyn SemanticDb> {}
|
||||
|
||||
#[salsa::jar(db=Db)]
|
||||
pub struct Jar(lint_syntax, lint_semantic, unwind_if_cancelled);
|
||||
@@ -1,53 +0,0 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use ruff_db::files::File;
|
||||
use ruff_db::system::{SystemPath, SystemPathBuf};
|
||||
|
||||
use crate::db::Jar;
|
||||
|
||||
pub mod db;
|
||||
pub mod lint;
|
||||
pub mod program;
|
||||
pub mod target_version;
|
||||
pub mod watch;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Workspace {
|
||||
root: SystemPathBuf,
|
||||
/// The files that are open in the workspace.
|
||||
///
|
||||
/// * Editor: The files that are actively being edited in the editor (the user has a tab open with the file).
|
||||
/// * CLI: The resolved files passed as arguments to the CLI.
|
||||
open_files: FxHashSet<File>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(root: SystemPathBuf) -> Self {
|
||||
Self {
|
||||
root,
|
||||
open_files: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root(&self) -> &SystemPath {
|
||||
self.root.as_path()
|
||||
}
|
||||
|
||||
// TODO having the content in workspace feels wrong.
|
||||
pub fn open_file(&mut self, file_id: File) {
|
||||
self.open_files.insert(file_id);
|
||||
}
|
||||
|
||||
pub fn close_file(&mut self, file_id: File) {
|
||||
self.open_files.remove(&file_id);
|
||||
}
|
||||
|
||||
// TODO introduce an `OpenFile` type instead of using an anonymous tuple.
|
||||
pub fn open_files(&self) -> impl Iterator<Item = File> + '_ {
|
||||
self.open_files.iter().copied()
|
||||
}
|
||||
|
||||
pub fn is_file_open(&self, file_id: File) -> bool {
|
||||
self.open_files.contains(&file_id)
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::time::Duration;
|
||||
|
||||
use tracing::trace_span;
|
||||
|
||||
use red_knot_module_resolver::ModuleName;
|
||||
use red_knot_python_semantic::types::Type;
|
||||
use red_knot_python_semantic::{HasTy, SemanticModel};
|
||||
use ruff_db::files::File;
|
||||
use ruff_db::parsed::{parsed_module, ParsedModule};
|
||||
use ruff_db::source::{source_text, SourceText};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::visitor::{walk_stmt, Visitor};
|
||||
|
||||
use crate::db::Db;
|
||||
|
||||
/// Workaround query to test for if the computation should be cancelled.
|
||||
/// Ideally, push for Salsa to expose an API for testing if cancellation was requested.
|
||||
#[salsa::tracked]
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn unwind_if_cancelled(db: &dyn Db) {}
|
||||
|
||||
#[salsa::tracked(return_ref)]
|
||||
pub(crate) fn lint_syntax(db: &dyn Db, file_id: File) -> Diagnostics {
|
||||
#[allow(clippy::print_stdout)]
|
||||
if std::env::var("RED_KNOT_SLOW_LINT").is_ok() {
|
||||
for i in 0..10 {
|
||||
unwind_if_cancelled(db);
|
||||
|
||||
println!("RED_KNOT_SLOW_LINT is set, sleeping for {i}/10 seconds");
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
|
||||
let mut diagnostics = Vec::new();
|
||||
|
||||
let source = source_text(db.upcast(), file_id);
|
||||
lint_lines(&source, &mut diagnostics);
|
||||
|
||||
let parsed = parsed_module(db.upcast(), file_id);
|
||||
|
||||
if parsed.errors().is_empty() {
|
||||
let ast = parsed.syntax();
|
||||
|
||||
let mut visitor = SyntaxLintVisitor {
|
||||
diagnostics,
|
||||
source: &source,
|
||||
};
|
||||
visitor.visit_body(&ast.body);
|
||||
diagnostics = visitor.diagnostics;
|
||||
} else {
|
||||
diagnostics.extend(parsed.errors().iter().map(ToString::to_string));
|
||||
}
|
||||
|
||||
Diagnostics::from(diagnostics)
|
||||
}
|
||||
|
||||
fn lint_lines(source: &str, diagnostics: &mut Vec<String>) {
|
||||
for (line_number, line) in source.lines().enumerate() {
|
||||
if line.len() < 88 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let char_count = line.chars().count();
|
||||
if char_count > 88 {
|
||||
diagnostics.push(format!(
|
||||
"Line {} is too long ({} characters)",
|
||||
line_number + 1,
|
||||
char_count
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(return_ref)]
|
||||
pub(crate) fn lint_semantic(db: &dyn Db, file_id: File) -> Diagnostics {
|
||||
let _span = trace_span!("lint_semantic", ?file_id).entered();
|
||||
|
||||
let source = source_text(db.upcast(), file_id);
|
||||
let parsed = parsed_module(db.upcast(), file_id);
|
||||
let semantic = SemanticModel::new(db.upcast(), file_id);
|
||||
|
||||
if !parsed.is_valid() {
|
||||
return Diagnostics::Empty;
|
||||
}
|
||||
|
||||
let context = SemanticLintContext {
|
||||
source,
|
||||
parsed,
|
||||
semantic,
|
||||
diagnostics: RefCell::new(Vec::new()),
|
||||
};
|
||||
|
||||
SemanticVisitor { context: &context }.visit_body(parsed.suite());
|
||||
|
||||
Diagnostics::from(context.diagnostics.take())
|
||||
}
|
||||
|
||||
fn lint_unresolved_imports(context: &SemanticLintContext, import: AnyImportRef) {
|
||||
match import {
|
||||
AnyImportRef::Import(import) => {
|
||||
for alias in &import.names {
|
||||
let ty = alias.ty(&context.semantic);
|
||||
|
||||
if ty.is_unknown() {
|
||||
context.push_diagnostic(format!("Unresolved import '{}'", &alias.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
AnyImportRef::ImportFrom(import) => {
|
||||
for alias in &import.names {
|
||||
let ty = alias.ty(&context.semantic);
|
||||
|
||||
if ty.is_unknown() {
|
||||
context.push_diagnostic(format!("Unresolved import '{}'", &alias.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_bad_override(context: &SemanticLintContext, class: &ast::StmtClassDef) {
|
||||
let semantic = &context.semantic;
|
||||
|
||||
// TODO we should have a special marker on the real typing module (from typeshed) so if you
|
||||
// have your own "typing" module in your project, we don't consider it THE typing module (and
|
||||
// same for other stdlib modules that our lint rules care about)
|
||||
let Some(typing) = semantic.resolve_module(ModuleName::new("typing").unwrap()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(typing_override) = semantic.public_symbol(&typing, "override") else {
|
||||
return;
|
||||
};
|
||||
|
||||
let override_ty = semantic.public_symbol_ty(typing_override);
|
||||
|
||||
let Type::Class(class_ty) = class.ty(semantic) else {
|
||||
return;
|
||||
};
|
||||
|
||||
for function in class
|
||||
.body
|
||||
.iter()
|
||||
.filter_map(|stmt| stmt.as_function_def_stmt())
|
||||
{
|
||||
let Type::Function(ty) = function.ty(semantic) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO this shouldn't make direct use of the Db; see comment on SemanticModel::db
|
||||
let db = semantic.db();
|
||||
|
||||
if ty.has_decorator(db, override_ty) {
|
||||
let method_name = ty.name(db);
|
||||
if class_ty.inherited_class_member(db, &method_name).is_none() {
|
||||
// TODO should have a qualname() method to support nested classes
|
||||
context.push_diagnostic(
|
||||
format!(
|
||||
"Method {}.{} is decorated with `typing.override` but does not override any base class method",
|
||||
class_ty.name(db),
|
||||
method_name,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SemanticLintContext<'a> {
|
||||
source: SourceText,
|
||||
parsed: &'a ParsedModule,
|
||||
semantic: SemanticModel<'a>,
|
||||
diagnostics: RefCell<Vec<String>>,
|
||||
}
|
||||
|
||||
impl<'db> SemanticLintContext<'db> {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn source_text(&self) -> &str {
|
||||
self.source.as_str()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn ast(&self) -> &'db ast::ModModule {
|
||||
self.parsed.syntax()
|
||||
}
|
||||
|
||||
pub(crate) fn push_diagnostic(&self, diagnostic: String) {
|
||||
self.diagnostics.borrow_mut().push(diagnostic);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn extend_diagnostics(&mut self, diagnostics: impl IntoIterator<Item = String>) {
|
||||
self.diagnostics.get_mut().extend(diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SyntaxLintVisitor<'a> {
|
||||
diagnostics: Vec<String>,
|
||||
source: &'a str,
|
||||
}
|
||||
|
||||
impl Visitor<'_> for SyntaxLintVisitor<'_> {
|
||||
fn visit_string_literal(&mut self, string_literal: &'_ ast::StringLiteral) {
|
||||
// A very naive implementation of use double quotes
|
||||
let text = &self.source[string_literal.range];
|
||||
|
||||
if text.starts_with('\'') {
|
||||
self.diagnostics
|
||||
.push("Use double quotes for strings".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SemanticVisitor<'a> {
|
||||
context: &'a SemanticLintContext<'a>,
|
||||
}
|
||||
|
||||
impl Visitor<'_> for SemanticVisitor<'_> {
|
||||
fn visit_stmt(&mut self, stmt: &ast::Stmt) {
|
||||
match stmt {
|
||||
ast::Stmt::ClassDef(class) => {
|
||||
lint_bad_override(self.context, class);
|
||||
}
|
||||
ast::Stmt::Import(import) => {
|
||||
lint_unresolved_imports(self.context, AnyImportRef::Import(import));
|
||||
}
|
||||
ast::Stmt::ImportFrom(import) => {
|
||||
lint_unresolved_imports(self.context, AnyImportRef::ImportFrom(import));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
walk_stmt(self, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Diagnostics {
|
||||
Empty,
|
||||
List(Vec<String>),
|
||||
}
|
||||
|
||||
impl Diagnostics {
|
||||
pub fn as_slice(&self) -> &[String] {
|
||||
match self {
|
||||
Diagnostics::Empty => &[],
|
||||
Diagnostics::List(list) => list.as_slice(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Diagnostics {
|
||||
type Target = [String];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<String>> for Diagnostics {
|
||||
fn from(value: Vec<String>) -> Self {
|
||||
if value.is_empty() {
|
||||
Diagnostics::Empty
|
||||
} else {
|
||||
Diagnostics::List(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum AnyImportRef<'a> {
|
||||
Import(&'a ast::StmtImport),
|
||||
ImportFrom(&'a ast::StmtImportFrom),
|
||||
}
|
||||
@@ -1,409 +0,0 @@
|
||||
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;
|
||||
use tracing_subscriber::layer::{Context, Filter, SubscriberExt};
|
||||
use tracing_subscriber::{Layer, Registry};
|
||||
use tracing_tree::time::Uptime;
|
||||
|
||||
use red_knot::program::{FileWatcherChange, Program};
|
||||
use red_knot::target_version::TargetVersion;
|
||||
use red_knot::watch::FileWatcher;
|
||||
use red_knot::Workspace;
|
||||
use red_knot_module_resolver::{set_module_resolution_settings, RawModuleResolutionSettings};
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(
|
||||
author,
|
||||
name = "red-knot",
|
||||
about = "An experimental multifile analysis backend for Ruff"
|
||||
)]
|
||||
#[command(version)]
|
||||
struct Args {
|
||||
#[clap(help = "File to check", required = true, value_name = "FILE")]
|
||||
entry_point: SystemPathBuf,
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "DIRECTORY",
|
||||
help = "Custom directory to use for stdlib typeshed stubs"
|
||||
)]
|
||||
custom_typeshed_dir: Option<SystemPathBuf>,
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "PATH",
|
||||
help = "Additional path to use as a module-resolution source (can be passed multiple times)"
|
||||
)]
|
||||
extra_search_path: Vec<SystemPathBuf>,
|
||||
#[arg(long, help = "Python version to assume when resolving types", default_value_t = TargetVersion::default(), value_name="VERSION")]
|
||||
target_version: TargetVersion,
|
||||
}
|
||||
|
||||
#[allow(
|
||||
clippy::print_stdout,
|
||||
clippy::unnecessary_wraps,
|
||||
clippy::print_stderr,
|
||||
clippy::dbg_macro
|
||||
)]
|
||||
pub fn main() -> anyhow::Result<()> {
|
||||
countme::enable(true);
|
||||
setup_tracing();
|
||||
|
||||
let Args {
|
||||
entry_point,
|
||||
custom_typeshed_dir,
|
||||
extra_search_path: extra_search_paths,
|
||||
target_version,
|
||||
} = Args::parse_from(std::env::args().collect::<Vec<_>>());
|
||||
|
||||
tracing::trace!("Target version: {target_version}");
|
||||
if let Some(custom_typeshed) = custom_typeshed_dir.as_ref() {
|
||||
tracing::trace!("Custom typeshed directory: {custom_typeshed}");
|
||||
}
|
||||
if !extra_search_paths.is_empty() {
|
||||
tracing::trace!("extra search paths: {extra_search_paths:?}");
|
||||
}
|
||||
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let cwd = SystemPath::from_std_path(&cwd).unwrap();
|
||||
let system = OsSystem::new(cwd);
|
||||
|
||||
if !system.path_exists(&entry_point) {
|
||||
eprintln!("The entry point does not exist.");
|
||||
return Err(anyhow::anyhow!("Invalid arguments"));
|
||||
}
|
||||
|
||||
if !system.is_file(&entry_point) {
|
||||
eprintln!("The entry point is not a file.");
|
||||
return Err(anyhow::anyhow!("Invalid arguments"));
|
||||
}
|
||||
|
||||
let workspace_folder = entry_point.parent().unwrap();
|
||||
let workspace = Workspace::new(workspace_folder.to_path_buf());
|
||||
|
||||
let workspace_search_path = workspace.root().to_path_buf();
|
||||
|
||||
let mut program = Program::new(workspace, system);
|
||||
|
||||
set_module_resolution_settings(
|
||||
&mut program,
|
||||
RawModuleResolutionSettings {
|
||||
extra_paths: extra_search_paths,
|
||||
workspace_root: workspace_search_path,
|
||||
site_packages: None,
|
||||
custom_typeshed: custom_typeshed_dir,
|
||||
target_version: red_knot_module_resolver::TargetVersion::from(target_version),
|
||||
},
|
||||
);
|
||||
|
||||
let entry_id = system_path_to_file(&program, entry_point.clone()).unwrap();
|
||||
program.workspace_mut().open_file(entry_id);
|
||||
|
||||
let (main_loop, main_loop_cancellation_token) = MainLoop::new();
|
||||
|
||||
// Listen to Ctrl+C and abort the watch mode.
|
||||
let main_loop_cancellation_token = Mutex::new(Some(main_loop_cancellation_token));
|
||||
ctrlc::set_handler(move || {
|
||||
let mut lock = main_loop_cancellation_token.lock().unwrap();
|
||||
|
||||
if let Some(token) = lock.take() {
|
||||
token.stop();
|
||||
}
|
||||
})?;
|
||||
|
||||
let file_changes_notifier = main_loop.file_changes_notifier();
|
||||
|
||||
// Watch for file changes and re-trigger the analysis.
|
||||
let mut file_watcher = FileWatcher::new(move |changes| {
|
||||
file_changes_notifier.notify(changes);
|
||||
})?;
|
||||
|
||||
file_watcher.watch_folder(workspace_folder.as_std_path())?;
|
||||
|
||||
main_loop.run(&mut program);
|
||||
|
||||
println!("{}", countme::get_all());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct MainLoop {
|
||||
orchestrator_sender: crossbeam_channel::Sender<OrchestratorMessage>,
|
||||
main_loop_receiver: crossbeam_channel::Receiver<MainLoopMessage>,
|
||||
}
|
||||
|
||||
impl MainLoop {
|
||||
fn new() -> (Self, MainLoopCancellationToken) {
|
||||
let (orchestrator_sender, orchestrator_receiver) = crossbeam_channel::bounded(1);
|
||||
let (main_loop_sender, main_loop_receiver) = crossbeam_channel::bounded(1);
|
||||
|
||||
let mut orchestrator = Orchestrator {
|
||||
receiver: orchestrator_receiver,
|
||||
sender: main_loop_sender.clone(),
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
std::thread::spawn(move || {
|
||||
orchestrator.run();
|
||||
});
|
||||
|
||||
(
|
||||
Self {
|
||||
orchestrator_sender,
|
||||
main_loop_receiver,
|
||||
},
|
||||
MainLoopCancellationToken {
|
||||
sender: main_loop_sender,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn file_changes_notifier(&self) -> FileChangesNotifier {
|
||||
FileChangesNotifier {
|
||||
sender: self.orchestrator_sender.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
fn run(self, program: &mut Program) {
|
||||
self.orchestrator_sender
|
||||
.send(OrchestratorMessage::Run)
|
||||
.unwrap();
|
||||
|
||||
for message in &self.main_loop_receiver {
|
||||
tracing::trace!("Main Loop: Tick");
|
||||
|
||||
match message {
|
||||
MainLoopMessage::CheckProgram { revision } => {
|
||||
let program = program.snapshot();
|
||||
let sender = self.orchestrator_sender.clone();
|
||||
|
||||
// Spawn a new task that checks the program. This needs to be done in a separate thread
|
||||
// to prevent blocking the main loop here.
|
||||
rayon::spawn(move || {
|
||||
if let Ok(result) = program.check() {
|
||||
sender
|
||||
.send(OrchestratorMessage::CheckProgramCompleted {
|
||||
diagnostics: result,
|
||||
revision,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
MainLoopMessage::ApplyChanges(changes) => {
|
||||
// Automatically cancels any pending queries and waits for them to complete.
|
||||
program.apply_changes(changes);
|
||||
}
|
||||
MainLoopMessage::CheckCompleted(diagnostics) => {
|
||||
eprintln!("{}", diagnostics.join("\n"));
|
||||
eprintln!("{}", countme::get_all());
|
||||
}
|
||||
MainLoopMessage::Exit => {
|
||||
eprintln!("{}", countme::get_all());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MainLoop {
|
||||
fn drop(&mut self) {
|
||||
self.orchestrator_sender
|
||||
.send(OrchestratorMessage::Shutdown)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileChangesNotifier {
|
||||
sender: crossbeam_channel::Sender<OrchestratorMessage>,
|
||||
}
|
||||
|
||||
impl FileChangesNotifier {
|
||||
fn notify(&self, changes: Vec<FileWatcherChange>) {
|
||||
self.sender
|
||||
.send(OrchestratorMessage::FileChanges(changes))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MainLoopCancellationToken {
|
||||
sender: crossbeam_channel::Sender<MainLoopMessage>,
|
||||
}
|
||||
|
||||
impl MainLoopCancellationToken {
|
||||
fn stop(self) {
|
||||
self.sender.send(MainLoopMessage::Exit).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct Orchestrator {
|
||||
/// Sends messages to the main loop.
|
||||
sender: crossbeam_channel::Sender<MainLoopMessage>,
|
||||
/// Receives messages from the main loop.
|
||||
receiver: crossbeam_channel::Receiver<OrchestratorMessage>,
|
||||
revision: usize,
|
||||
}
|
||||
|
||||
impl Orchestrator {
|
||||
#[allow(clippy::print_stderr)]
|
||||
fn run(&mut self) {
|
||||
while let Ok(message) = self.receiver.recv() {
|
||||
match message {
|
||||
OrchestratorMessage::Run => {
|
||||
self.sender
|
||||
.send(MainLoopMessage::CheckProgram {
|
||||
revision: self.revision,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
OrchestratorMessage::CheckProgramCompleted {
|
||||
diagnostics,
|
||||
revision,
|
||||
} => {
|
||||
// Only take the diagnostics if they are for the latest revision.
|
||||
if self.revision == revision {
|
||||
self.sender
|
||||
.send(MainLoopMessage::CheckCompleted(diagnostics))
|
||||
.unwrap();
|
||||
} else {
|
||||
tracing::debug!("Discarding diagnostics for outdated revision {revision} (current: {}).", self.revision);
|
||||
}
|
||||
}
|
||||
|
||||
OrchestratorMessage::FileChanges(changes) => {
|
||||
// Request cancellation, but wait until all analysis tasks have completed to
|
||||
// avoid stale messages in the next main loop.
|
||||
|
||||
self.revision += 1;
|
||||
self.debounce_changes(changes);
|
||||
}
|
||||
OrchestratorMessage::Shutdown => {
|
||||
return self.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn debounce_changes(&self, mut changes: Vec<FileWatcherChange>) {
|
||||
loop {
|
||||
// Consume possibly incoming file change messages before running a new analysis, but don't wait for more than 100ms.
|
||||
crossbeam_channel::select! {
|
||||
recv(self.receiver) -> message => {
|
||||
match message {
|
||||
Ok(OrchestratorMessage::Shutdown) => {
|
||||
return self.shutdown();
|
||||
}
|
||||
Ok(OrchestratorMessage::FileChanges(file_changes)) => {
|
||||
changes.extend(file_changes);
|
||||
}
|
||||
|
||||
Ok(OrchestratorMessage::CheckProgramCompleted { .. })=> {
|
||||
// disregard any outdated completion message.
|
||||
}
|
||||
Ok(OrchestratorMessage::Run) => unreachable!("The orchestrator is already running."),
|
||||
|
||||
Err(_) => {
|
||||
// There are no more senders, no point in waiting for more messages
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
default(std::time::Duration::from_millis(10)) => {
|
||||
// No more file changes after 10 ms, send the changes and schedule a new analysis
|
||||
self.sender.send(MainLoopMessage::ApplyChanges(changes)).unwrap();
|
||||
self.sender.send(MainLoopMessage::CheckProgram { revision: self.revision}).unwrap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
fn shutdown(&self) {
|
||||
tracing::trace!("Shutting down orchestrator.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Message sent from the orchestrator to the main loop.
|
||||
#[derive(Debug)]
|
||||
enum MainLoopMessage {
|
||||
CheckProgram { revision: usize },
|
||||
CheckCompleted(Vec<String>),
|
||||
ApplyChanges(Vec<FileWatcherChange>),
|
||||
Exit,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum OrchestratorMessage {
|
||||
Run,
|
||||
Shutdown,
|
||||
|
||||
CheckProgramCompleted {
|
||||
diagnostics: Vec<String>,
|
||||
revision: usize,
|
||||
},
|
||||
|
||||
FileChanges(Vec<FileWatcherChange>),
|
||||
}
|
||||
|
||||
fn setup_tracing() {
|
||||
let subscriber = Registry::default().with(
|
||||
tracing_tree::HierarchicalLayer::default()
|
||||
.with_indent_lines(true)
|
||||
.with_indent_amount(2)
|
||||
.with_bracketed_fields(true)
|
||||
.with_thread_ids(true)
|
||||
.with_targets(true)
|
||||
.with_writer(|| Box::new(std::io::stderr()))
|
||||
.with_timer(Uptime::default())
|
||||
.with_filter(LoggingFilter {
|
||||
trace_level: Level::TRACE,
|
||||
}),
|
||||
);
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||
}
|
||||
|
||||
struct LoggingFilter {
|
||||
trace_level: Level,
|
||||
}
|
||||
|
||||
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 {
|
||||
Level::INFO
|
||||
};
|
||||
|
||||
meta.level() <= &filter
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Filter<S> for LoggingFilter {
|
||||
fn enabled(&self, meta: &Metadata<'_>, _cx: &Context<'_, S>) -> bool {
|
||||
self.is_enabled(meta)
|
||||
}
|
||||
|
||||
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
|
||||
if self.is_enabled(meta) {
|
||||
Interest::always()
|
||||
} else {
|
||||
Interest::never()
|
||||
}
|
||||
}
|
||||
|
||||
fn max_level_hint(&self) -> Option<LevelFilter> {
|
||||
Some(LevelFilter::from_level(self.trace_level))
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use ruff_db::files::File;
|
||||
use salsa::Cancelled;
|
||||
|
||||
use crate::lint::{lint_semantic, lint_syntax, Diagnostics};
|
||||
use crate::program::Program;
|
||||
|
||||
impl Program {
|
||||
/// Checks all open files in the workspace and its dependencies.
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
pub fn check(&self) -> Result<Vec<String>, Cancelled> {
|
||||
self.with_db(|db| {
|
||||
let mut result = Vec::new();
|
||||
for open_file in db.workspace.open_files() {
|
||||
result.extend_from_slice(&db.check_file_impl(open_file));
|
||||
}
|
||||
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
pub fn check_file(&self, file: File) -> Result<Diagnostics, Cancelled> {
|
||||
self.with_db(|db| db.check_file_impl(file))
|
||||
}
|
||||
|
||||
fn check_file_impl(&self, file: File) -> Diagnostics {
|
||||
let mut diagnostics = Vec::new();
|
||||
diagnostics.extend_from_slice(lint_syntax(self, file));
|
||||
diagnostics.extend_from_slice(lint_semantic(self, file));
|
||||
Diagnostics::from(diagnostics)
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
use std::panic::{AssertUnwindSafe, RefUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
|
||||
use salsa::{Cancelled, Database};
|
||||
|
||||
use red_knot_module_resolver::{vendored_typeshed_stubs, Db as ResolverDb, Jar as ResolverJar};
|
||||
use red_knot_python_semantic::{Db as SemanticDb, Jar as SemanticJar};
|
||||
use ruff_db::files::{File, Files};
|
||||
use ruff_db::system::{System, SystemPathBuf};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_db::{Db as SourceDb, Jar as SourceJar, Upcast};
|
||||
|
||||
use crate::db::{Db, Jar};
|
||||
use crate::Workspace;
|
||||
|
||||
mod check;
|
||||
|
||||
#[salsa::db(SourceJar, ResolverJar, SemanticJar, Jar)]
|
||||
pub struct Program {
|
||||
storage: salsa::Storage<Program>,
|
||||
files: Files,
|
||||
system: Arc<dyn System + Send + Sync + RefUnwindSafe>,
|
||||
workspace: Workspace,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new<S>(workspace: Workspace, system: S) -> Self
|
||||
where
|
||||
S: System + 'static + Send + Sync + RefUnwindSafe,
|
||||
{
|
||||
Self {
|
||||
storage: salsa::Storage::default(),
|
||||
files: Files::default(),
|
||||
system: Arc::new(system),
|
||||
workspace,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_changes<I>(&mut self, changes: I)
|
||||
where
|
||||
I: IntoIterator<Item = FileWatcherChange>,
|
||||
{
|
||||
for change in changes {
|
||||
File::touch_path(self, &change.path);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn workspace(&self) -> &Workspace {
|
||||
&self.workspace
|
||||
}
|
||||
|
||||
pub fn workspace_mut(&mut self) -> &mut Workspace {
|
||||
&mut self.workspace
|
||||
}
|
||||
|
||||
fn with_db<F, T>(&self, f: F) -> Result<T, Cancelled>
|
||||
where
|
||||
F: FnOnce(&Program) -> T + std::panic::UnwindSafe,
|
||||
{
|
||||
// The `AssertUnwindSafe` here looks scary, but is a consequence of Salsa's design.
|
||||
// Salsa uses panics to implement cancellation and to recover from cycles. However, the Salsa
|
||||
// storage isn't `UnwindSafe` or `RefUnwindSafe` because its dependencies `DashMap` and `parking_lot::*` aren't
|
||||
// unwind safe.
|
||||
//
|
||||
// Having to use `AssertUnwindSafe` isn't as big as a deal as it might seem because
|
||||
// the `UnwindSafe` and `RefUnwindSafe` traits are designed to catch logical bugs.
|
||||
// They don't protect against [UB](https://internals.rust-lang.org/t/pre-rfc-deprecating-unwindsafe/15974).
|
||||
// On top of that, `Cancelled` only catches specific Salsa-panics and propagates all other panics.
|
||||
//
|
||||
// That still leaves us with possible logical bugs in two sources:
|
||||
// * In Salsa itself: This must be considered a bug in Salsa and needs fixing upstream.
|
||||
// Reviewing Salsa code specifically around unwind safety seems doable.
|
||||
// * Our code: This is the main concern. Luckily, it only involves code that uses internal mutability
|
||||
// and calls into Salsa queries when mutating the internal state. Using `AssertUnwindSafe`
|
||||
// certainly makes it harder to catch these issues in our user code.
|
||||
//
|
||||
// For now, this is the only solution at hand unless Salsa decides to change its design.
|
||||
// [Zulip support thread](https://salsa.zulipchat.com/#narrow/stream/145099-general/topic/How.20to.20use.20.60Cancelled.3A.3Acatch.60)
|
||||
let db = &AssertUnwindSafe(self);
|
||||
Cancelled::catch(|| f(db))
|
||||
}
|
||||
}
|
||||
|
||||
impl Upcast<dyn SemanticDb> for Program {
|
||||
fn upcast(&self) -> &(dyn SemanticDb + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Upcast<dyn SourceDb> for Program {
|
||||
fn upcast(&self) -> &(dyn SourceDb + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Upcast<dyn ResolverDb> for Program {
|
||||
fn upcast(&self) -> &(dyn ResolverDb + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ResolverDb for Program {}
|
||||
|
||||
impl SemanticDb for Program {}
|
||||
|
||||
impl SourceDb for Program {
|
||||
fn vendored(&self) -> &VendoredFileSystem {
|
||||
vendored_typeshed_stubs()
|
||||
}
|
||||
|
||||
fn system(&self) -> &dyn System {
|
||||
&*self.system
|
||||
}
|
||||
|
||||
fn files(&self) -> &Files {
|
||||
&self.files
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for Program {}
|
||||
|
||||
impl Db for Program {}
|
||||
|
||||
impl salsa::ParallelDatabase for Program {
|
||||
fn snapshot(&self) -> salsa::Snapshot<Self> {
|
||||
salsa::Snapshot::new(Self {
|
||||
storage: self.storage.snapshot(),
|
||||
files: self.files.snapshot(),
|
||||
system: self.system.clone(),
|
||||
workspace: self.workspace.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FileWatcherChange {
|
||||
path: SystemPathBuf,
|
||||
#[allow(unused)]
|
||||
kind: FileChangeKind,
|
||||
}
|
||||
|
||||
impl FileWatcherChange {
|
||||
pub fn new(path: SystemPathBuf, kind: FileChangeKind) -> Self {
|
||||
Self { path, kind }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum FileChangeKind {
|
||||
Created,
|
||||
Modified,
|
||||
Deleted,
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
/// Enumeration of all supported Python versions
|
||||
///
|
||||
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
|
||||
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default, clap::ValueEnum)]
|
||||
pub enum TargetVersion {
|
||||
Py37,
|
||||
#[default]
|
||||
Py38,
|
||||
Py39,
|
||||
Py310,
|
||||
Py311,
|
||||
Py312,
|
||||
Py313,
|
||||
}
|
||||
|
||||
impl TargetVersion {
|
||||
const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Py37 => "py37",
|
||||
Self::Py38 => "py38",
|
||||
Self::Py39 => "py39",
|
||||
Self::Py310 => "py310",
|
||||
Self::Py311 => "py311",
|
||||
Self::Py312 => "py312",
|
||||
Self::Py313 => "py313",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetVersion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetVersion> for red_knot_module_resolver::TargetVersion {
|
||||
fn from(value: TargetVersion) -> Self {
|
||||
match value {
|
||||
TargetVersion::Py37 => red_knot_module_resolver::TargetVersion::Py37,
|
||||
TargetVersion::Py38 => red_knot_module_resolver::TargetVersion::Py38,
|
||||
TargetVersion::Py39 => red_knot_module_resolver::TargetVersion::Py39,
|
||||
TargetVersion::Py310 => red_knot_module_resolver::TargetVersion::Py310,
|
||||
TargetVersion::Py311 => red_knot_module_resolver::TargetVersion::Py311,
|
||||
TargetVersion::Py312 => red_knot_module_resolver::TargetVersion::Py312,
|
||||
TargetVersion::Py313 => red_knot_module_resolver::TargetVersion::Py313,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Context;
|
||||
use notify::event::{CreateKind, RemoveKind};
|
||||
use notify::{recommended_watcher, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
|
||||
use ruff_db::system::SystemPath;
|
||||
|
||||
use crate::program::{FileChangeKind, FileWatcherChange};
|
||||
|
||||
pub struct FileWatcher {
|
||||
watcher: RecommendedWatcher,
|
||||
}
|
||||
|
||||
pub trait EventHandler: Send + 'static {
|
||||
fn handle(&self, changes: Vec<FileWatcherChange>);
|
||||
}
|
||||
|
||||
impl<F> EventHandler for F
|
||||
where
|
||||
F: Fn(Vec<FileWatcherChange>) + Send + 'static,
|
||||
{
|
||||
fn handle(&self, changes: Vec<FileWatcherChange>) {
|
||||
let f = self;
|
||||
f(changes);
|
||||
}
|
||||
}
|
||||
|
||||
impl FileWatcher {
|
||||
pub fn new<E>(handler: E) -> anyhow::Result<Self>
|
||||
where
|
||||
E: EventHandler,
|
||||
{
|
||||
Self::from_handler(Box::new(handler))
|
||||
}
|
||||
|
||||
fn from_handler(handler: Box<dyn EventHandler>) -> anyhow::Result<Self> {
|
||||
let watcher = recommended_watcher(move |changes: notify::Result<Event>| {
|
||||
match changes {
|
||||
Ok(event) => {
|
||||
// TODO verify that this handles all events correctly
|
||||
let change_kind = match event.kind {
|
||||
EventKind::Create(CreateKind::File) => FileChangeKind::Created,
|
||||
EventKind::Modify(_) => FileChangeKind::Modified,
|
||||
EventKind::Remove(RemoveKind::File) => FileChangeKind::Deleted,
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut changes = Vec::new();
|
||||
|
||||
for path in event.paths {
|
||||
if path.is_file() {
|
||||
if let Some(fs_path) = SystemPath::from_std_path(&path) {
|
||||
changes.push(FileWatcherChange::new(
|
||||
fs_path.to_path_buf(),
|
||||
change_kind,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !changes.is_empty() {
|
||||
handler.handle(changes);
|
||||
}
|
||||
}
|
||||
// TODO proper error handling
|
||||
Err(err) => {
|
||||
panic!("Error: {err}");
|
||||
}
|
||||
}
|
||||
})
|
||||
.context("Failed to create file watcher.")?;
|
||||
|
||||
Ok(Self { watcher })
|
||||
}
|
||||
|
||||
pub fn watch_folder(&mut self, path: &Path) -> anyhow::Result<()> {
|
||||
self.watcher.watch(path, RecursiveMode::Recursive)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
[package]
|
||||
name = "red_knot_module_resolver"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
documentation = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
ruff_db = { workspace = true }
|
||||
ruff_python_stdlib = { workspace = true }
|
||||
|
||||
compact_str = { workspace = true }
|
||||
camino = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
salsa = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
path-slash = { workspace = true }
|
||||
walkdir = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
insta = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
walkdir = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,9 +0,0 @@
|
||||
# Red Knot
|
||||
|
||||
A work-in-progress multifile module resolver for Ruff.
|
||||
|
||||
## Vendored types for the stdlib
|
||||
|
||||
This crate vendors [typeshed](https://github.com/python/typeshed)'s stubs for the standard library. The vendored stubs can be found in `crates/red_knot_module_resolver/vendor/typeshed`. The file `crates/red_knot_module_resolver/vendor/typeshed/source_commit.txt` tells you the typeshed commit that our vendored stdlib stubs currently correspond to.
|
||||
|
||||
The typeshed stubs are updated every two weeks via an automated PR using the `sync_typeshed.yaml` workflow in the `.github/workflows` directory. This workflow can also be triggered at any time via [workflow dispatch](https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow#running-a-workflow).
|
||||
@@ -1,74 +0,0 @@
|
||||
//! Build script to package our vendored typeshed files
|
||||
//! into a zip archive that can be included in the Ruff binary.
|
||||
//!
|
||||
//! This script should be automatically run at build time
|
||||
//! whenever the script itself changes, or whenever any files
|
||||
//! in `crates/red_knot_module_resolver/vendor/typeshed` change.
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
use path_slash::PathExt;
|
||||
use zip::result::ZipResult;
|
||||
use zip::write::{FileOptions, ZipWriter};
|
||||
use zip::CompressionMethod;
|
||||
|
||||
const TYPESHED_SOURCE_DIR: &str = "vendor/typeshed";
|
||||
const TYPESHED_ZIP_LOCATION: &str = "/zipped_typeshed.zip";
|
||||
|
||||
/// Recursively zip the contents of an entire directory.
|
||||
///
|
||||
/// This routine is adapted from a recipe at
|
||||
/// <https://github.com/zip-rs/zip-old/blob/5d0f198124946b7be4e5969719a7f29f363118cd/examples/write_dir.rs>
|
||||
fn zip_dir(directory_path: &str, writer: File) -> ZipResult<File> {
|
||||
let mut zip = ZipWriter::new(writer);
|
||||
|
||||
let options = FileOptions::default()
|
||||
.compression_method(CompressionMethod::Zstd)
|
||||
.unix_permissions(0o644);
|
||||
|
||||
for entry in walkdir::WalkDir::new(directory_path) {
|
||||
let dir_entry = entry.unwrap();
|
||||
let absolute_path = dir_entry.path();
|
||||
let normalized_relative_path = absolute_path
|
||||
.strip_prefix(Path::new(directory_path))
|
||||
.unwrap()
|
||||
.to_slash()
|
||||
.expect("Unexpected non-utf8 typeshed path!");
|
||||
|
||||
// Write file or directory explicitly
|
||||
// Some unzip tools unzip files with directory paths correctly, some do not!
|
||||
if absolute_path.is_file() {
|
||||
println!("adding file {absolute_path:?} as {normalized_relative_path:?} ...");
|
||||
zip.start_file(normalized_relative_path, options)?;
|
||||
let mut f = File::open(absolute_path)?;
|
||||
std::io::copy(&mut f, &mut zip).unwrap();
|
||||
} else if !normalized_relative_path.is_empty() {
|
||||
// Only if not root! Avoids path spec / warning
|
||||
// and mapname conversion failed error on unzip
|
||||
println!("adding dir {absolute_path:?} as {normalized_relative_path:?} ...");
|
||||
zip.add_directory(normalized_relative_path, options)?;
|
||||
}
|
||||
}
|
||||
zip.finish()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed={TYPESHED_SOURCE_DIR}");
|
||||
assert!(
|
||||
Path::new(TYPESHED_SOURCE_DIR).is_dir(),
|
||||
"Where is typeshed?"
|
||||
);
|
||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||
|
||||
// N.B. Deliberately using `format!()` instead of `Path::join()` here,
|
||||
// so that we use `/` as a path separator on all platforms.
|
||||
// That enables us to load the typeshed zip at compile time in `module.rs`
|
||||
// (otherwise we'd have to dynamically determine the exact path to the typeshed zip
|
||||
// based on the default path separator for the specific platform we're on,
|
||||
// which can't be done at compile time.)
|
||||
let zipped_typeshed_location = format!("{out_dir}{TYPESHED_ZIP_LOCATION}");
|
||||
|
||||
let zipped_typeshed = File::create(zipped_typeshed_location).unwrap();
|
||||
zip_dir(TYPESHED_SOURCE_DIR, zipped_typeshed).unwrap();
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
use ruff_db::Upcast;
|
||||
|
||||
use crate::resolver::{
|
||||
file_to_module,
|
||||
internal::{ModuleNameIngredient, ModuleResolverSettings},
|
||||
resolve_module_query,
|
||||
};
|
||||
use crate::typeshed::parse_typeshed_versions;
|
||||
|
||||
#[salsa::jar(db=Db)]
|
||||
pub struct Jar(
|
||||
ModuleNameIngredient<'_>,
|
||||
ModuleResolverSettings,
|
||||
resolve_module_query,
|
||||
file_to_module,
|
||||
parse_typeshed_versions,
|
||||
);
|
||||
|
||||
pub trait Db: salsa::DbWithJar<Jar> + 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;
|
||||
|
||||
use crate::vendored_typeshed_stubs;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[salsa::db(Jar, ruff_db::Jar)]
|
||||
pub(crate) struct TestDb {
|
||||
storage: salsa::Storage<Self>,
|
||||
system: TestSystem,
|
||||
vendored: VendoredFileSystem,
|
||||
files: Files,
|
||||
events: sync::Arc<sync::Mutex<Vec<salsa::Event>>>,
|
||||
}
|
||||
|
||||
impl TestDb {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
storage: salsa::Storage::default(),
|
||||
system: TestSystem::default(),
|
||||
vendored: vendored_typeshed_stubs().snapshot(),
|
||||
events: sync::Arc::default(),
|
||||
files: Files::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the salsa events.
|
||||
///
|
||||
/// ## Panics
|
||||
/// If there are any pending salsa snapshots.
|
||||
pub(crate) fn take_salsa_events(&mut self) -> Vec<salsa::Event> {
|
||||
let inner = sync::Arc::get_mut(&mut self.events).expect("no pending salsa snapshots");
|
||||
|
||||
let events = inner.get_mut().unwrap();
|
||||
std::mem::take(&mut *events)
|
||||
}
|
||||
|
||||
/// Clears the salsa events.
|
||||
///
|
||||
/// ## Panics
|
||||
/// If there are any pending salsa snapshots.
|
||||
pub(crate) fn clear_salsa_events(&mut self) {
|
||||
self.take_salsa_events();
|
||||
}
|
||||
}
|
||||
|
||||
impl Upcast<dyn ruff_db::Db> for TestDb {
|
||||
fn upcast(&self) -> &(dyn ruff_db::Db + 'static) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ruff_db::Db for TestDb {
|
||||
fn vendored(&self) -> &VendoredFileSystem {
|
||||
&self.vendored
|
||||
}
|
||||
|
||||
fn system(&self) -> &dyn ruff_db::system::System {
|
||||
&self.system
|
||||
}
|
||||
|
||||
fn files(&self) -> &Files {
|
||||
&self.files
|
||||
}
|
||||
}
|
||||
|
||||
impl Db for TestDb {}
|
||||
|
||||
impl DbWithTestSystem for TestDb {
|
||||
fn test_system(&self) -> &TestSystem {
|
||||
&self.system
|
||||
}
|
||||
|
||||
fn test_system_mut(&mut self) -> &mut TestSystem {
|
||||
&mut self.system
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
mod db;
|
||||
mod module;
|
||||
mod module_name;
|
||||
mod path;
|
||||
mod resolver;
|
||||
mod state;
|
||||
mod supported_py_version;
|
||||
mod typeshed;
|
||||
|
||||
#[cfg(test)]
|
||||
mod testing;
|
||||
|
||||
pub use db::{Db, Jar};
|
||||
pub use module::{Module, ModuleKind};
|
||||
pub use module_name::ModuleName;
|
||||
pub use resolver::{resolve_module, set_module_resolution_settings, RawModuleResolutionSettings};
|
||||
pub use supported_py_version::TargetVersion;
|
||||
pub use typeshed::{
|
||||
vendored_typeshed_stubs, TypeshedVersionsParseError, TypeshedVersionsParseErrorKind,
|
||||
};
|
||||
@@ -1,91 +0,0 @@
|
||||
use std::fmt::Formatter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruff_db::files::File;
|
||||
|
||||
use crate::db::Db;
|
||||
use crate::module_name::ModuleName;
|
||||
use crate::path::{ModuleResolutionPathBuf, ModuleResolutionPathRef};
|
||||
|
||||
/// Representation of a Python module.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Module {
|
||||
inner: Arc<ModuleInner>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub(crate) fn new(
|
||||
name: ModuleName,
|
||||
kind: ModuleKind,
|
||||
search_path: Arc<ModuleResolutionPathBuf>,
|
||||
file: File,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(ModuleInner {
|
||||
name,
|
||||
kind,
|
||||
search_path,
|
||||
file,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// The absolute name of the module (e.g. `foo.bar`)
|
||||
pub fn name(&self) -> &ModuleName {
|
||||
&self.inner.name
|
||||
}
|
||||
|
||||
/// The file to the source code that defines this module
|
||||
pub fn file(&self) -> File {
|
||||
self.inner.file
|
||||
}
|
||||
|
||||
/// The search path from which the module was resolved.
|
||||
pub(crate) fn search_path(&self) -> ModuleResolutionPathRef {
|
||||
ModuleResolutionPathRef::from(&*self.inner.search_path)
|
||||
}
|
||||
|
||||
/// Determine whether this module is a single-file module or a package
|
||||
pub fn kind(&self) -> ModuleKind {
|
||||
self.inner.kind
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Module {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Module")
|
||||
.field("name", &self.name())
|
||||
.field("kind", &self.kind())
|
||||
.field("file", &self.file())
|
||||
.field("search_path", &self.search_path())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
kind: ModuleKind,
|
||||
search_path: Arc<ModuleResolutionPathBuf>,
|
||||
file: File,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum ModuleKind {
|
||||
/// A single-file module (e.g. `foo.py` or `foo.pyi`)
|
||||
Module,
|
||||
|
||||
/// A python package (`foo/__init__.py` or `foo/__init__.pyi`)
|
||||
Package,
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
use compact_str::{CompactString, ToCompactString};
|
||||
|
||||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
|
||||
/// A module name, e.g. `foo.bar`.
|
||||
///
|
||||
/// Always normalized to the absolute form (never a relative module name, i.e., never `.foo`).
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
pub struct ModuleName(compact_str::CompactString);
|
||||
|
||||
impl ModuleName {
|
||||
/// Creates a new module name for `name`. Returns `Some` if `name` is a valid, absolute
|
||||
/// module name and `None` otherwise.
|
||||
///
|
||||
/// The module name is invalid if:
|
||||
///
|
||||
/// * The name is empty
|
||||
/// * The name is relative
|
||||
/// * The name ends with a `.`
|
||||
/// * The name contains a sequence of multiple dots
|
||||
/// * A component of a name (the part between two dots) isn't a valid python identifier.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(name: &str) -> Option<Self> {
|
||||
Self::is_valid_name(name).then(|| Self(CompactString::from(name)))
|
||||
}
|
||||
|
||||
/// Creates a new module name for `name` where `name` is a static string.
|
||||
/// Returns `Some` if `name` is a valid, absolute module name and `None` otherwise.
|
||||
///
|
||||
/// The module name is invalid if:
|
||||
///
|
||||
/// * The name is empty
|
||||
/// * The name is relative
|
||||
/// * The name ends with a `.`
|
||||
/// * The name contains a sequence of multiple dots
|
||||
/// * A component of a name (the part between two dots) isn't a valid python identifier.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// use red_knot_module_resolver::ModuleName;
|
||||
///
|
||||
/// assert_eq!(ModuleName::new_static("foo.bar").as_deref(), Some("foo.bar"));
|
||||
/// assert_eq!(ModuleName::new_static(""), None);
|
||||
/// assert_eq!(ModuleName::new_static("..foo"), None);
|
||||
/// assert_eq!(ModuleName::new_static(".foo"), None);
|
||||
/// assert_eq!(ModuleName::new_static("foo."), None);
|
||||
/// assert_eq!(ModuleName::new_static("foo..bar"), None);
|
||||
/// assert_eq!(ModuleName::new_static("2000"), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new_static(name: &'static str) -> Option<Self> {
|
||||
// TODO(Micha): Use CompactString::const_new once we upgrade to 0.8 https://github.com/ParkMyCar/compact_str/pull/336
|
||||
Self::is_valid_name(name).then(|| Self(CompactString::from(name)))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn is_valid_name(name: &str) -> bool {
|
||||
!name.is_empty() && name.split('.').all(is_identifier)
|
||||
}
|
||||
|
||||
/// An iterator over the components of the module name:
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use red_knot_module_resolver::ModuleName;
|
||||
///
|
||||
/// assert_eq!(ModuleName::new_static("foo.bar.baz").unwrap().components().collect::<Vec<_>>(), vec!["foo", "bar", "baz"]);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn components(&self) -> impl DoubleEndedIterator<Item = &str> {
|
||||
self.0.split('.')
|
||||
}
|
||||
|
||||
/// The name of this module's immediate parent, if it has a parent.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use red_knot_module_resolver::ModuleName;
|
||||
///
|
||||
/// assert_eq!(ModuleName::new_static("foo.bar").unwrap().parent(), Some(ModuleName::new_static("foo").unwrap()));
|
||||
/// assert_eq!(ModuleName::new_static("foo.bar.baz").unwrap().parent(), Some(ModuleName::new_static("foo.bar").unwrap()));
|
||||
/// assert_eq!(ModuleName::new_static("root").unwrap().parent(), None);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn parent(&self) -> Option<ModuleName> {
|
||||
let (parent, _) = self.0.rsplit_once('.')?;
|
||||
Some(Self(parent.to_compact_string()))
|
||||
}
|
||||
|
||||
/// Returns `true` if the name starts with `other`.
|
||||
///
|
||||
/// This is equivalent to checking if `self` is a sub-module of `other`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use red_knot_module_resolver::ModuleName;
|
||||
///
|
||||
/// assert!(ModuleName::new_static("foo.bar").unwrap().starts_with(&ModuleName::new_static("foo").unwrap()));
|
||||
///
|
||||
/// assert!(!ModuleName::new_static("foo.bar").unwrap().starts_with(&ModuleName::new_static("bar").unwrap()));
|
||||
/// assert!(!ModuleName::new_static("foo_bar").unwrap().starts_with(&ModuleName::new_static("foo").unwrap()));
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn starts_with(&self, other: &ModuleName) -> bool {
|
||||
let mut self_components = self.components();
|
||||
let other_components = other.components();
|
||||
|
||||
for other_component in other_components {
|
||||
if self_components.next() != Some(other_component) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Construct a [`ModuleName`] from a sequence of parts.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use red_knot_module_resolver::ModuleName;
|
||||
///
|
||||
/// assert_eq!(&*ModuleName::from_components(["a"]).unwrap(), "a");
|
||||
/// assert_eq!(&*ModuleName::from_components(["a", "b"]).unwrap(), "a.b");
|
||||
/// assert_eq!(&*ModuleName::from_components(["a", "b", "c"]).unwrap(), "a.b.c");
|
||||
///
|
||||
/// assert_eq!(ModuleName::from_components(["a-b"]), None);
|
||||
/// assert_eq!(ModuleName::from_components(["a", "a-b"]), None);
|
||||
/// assert_eq!(ModuleName::from_components(["a", "b", "a-b-c"]), None);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn from_components<'a>(components: impl IntoIterator<Item = &'a str>) -> Option<Self> {
|
||||
let mut components = components.into_iter();
|
||||
let first_part = components.next()?;
|
||||
if !is_identifier(first_part) {
|
||||
return None;
|
||||
}
|
||||
let name = if let Some(second_part) = components.next() {
|
||||
if !is_identifier(second_part) {
|
||||
return None;
|
||||
}
|
||||
let mut name = format!("{first_part}.{second_part}");
|
||||
for part in components {
|
||||
if !is_identifier(part) {
|
||||
return None;
|
||||
}
|
||||
name.push('.');
|
||||
name.push_str(part);
|
||||
}
|
||||
CompactString::from(&name)
|
||||
} else {
|
||||
CompactString::from(first_part)
|
||||
};
|
||||
Some(Self(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ModuleName {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for ModuleName {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ModuleName> for str {
|
||||
fn eq(&self, other: &ModuleName) -> bool {
|
||||
self == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ModuleName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
||||
use ruff_db::system::System;
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
|
||||
use crate::db::Db;
|
||||
use crate::supported_py_version::TargetVersion;
|
||||
use crate::typeshed::LazyTypeshedVersions;
|
||||
|
||||
pub(crate) struct ResolverState<'db> {
|
||||
pub(crate) db: &'db dyn Db,
|
||||
pub(crate) typeshed_versions: LazyTypeshedVersions<'db>,
|
||||
pub(crate) target_version: TargetVersion,
|
||||
}
|
||||
|
||||
impl<'db> ResolverState<'db> {
|
||||
pub(crate) fn new(db: &'db dyn Db, target_version: TargetVersion) -> Self {
|
||||
Self {
|
||||
db,
|
||||
typeshed_versions: LazyTypeshedVersions::new(),
|
||||
target_version,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn system(&self) -> &dyn System {
|
||||
self.db.system()
|
||||
}
|
||||
|
||||
pub(crate) fn vendored(&self) -> &VendoredFileSystem {
|
||||
self.db.vendored()
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/// Enumeration of all supported Python versions
|
||||
///
|
||||
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
|
||||
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub enum TargetVersion {
|
||||
Py37,
|
||||
#[default]
|
||||
Py38,
|
||||
Py39,
|
||||
Py310,
|
||||
Py311,
|
||||
Py312,
|
||||
Py313,
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPath, SystemPathBuf};
|
||||
use ruff_db::vendored::VendoredPathBuf;
|
||||
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::resolver::{set_module_resolution_settings, RawModuleResolutionSettings};
|
||||
use crate::supported_py_version::TargetVersion;
|
||||
|
||||
/// A test case for the module resolver.
|
||||
///
|
||||
/// You generally shouldn't construct instances of this struct directly;
|
||||
/// instead, use the [`TestCaseBuilder`].
|
||||
pub(crate) struct TestCase<T> {
|
||||
pub(crate) db: TestDb,
|
||||
pub(crate) src: SystemPathBuf,
|
||||
pub(crate) stdlib: T,
|
||||
pub(crate) site_packages: SystemPathBuf,
|
||||
pub(crate) target_version: TargetVersion,
|
||||
}
|
||||
|
||||
/// A `(file_name, file_contents)` tuple
|
||||
pub(crate) type FileSpec = (&'static str, &'static str);
|
||||
|
||||
/// Specification for a typeshed mock to be created as part of a test
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub(crate) struct MockedTypeshed {
|
||||
/// The stdlib files to be created in the typeshed mock
|
||||
pub(crate) stdlib_files: &'static [FileSpec],
|
||||
|
||||
/// The contents of the `stdlib/VERSIONS` file
|
||||
/// to be created in the typeshed mock
|
||||
pub(crate) versions: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct VendoredTypeshed;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UnspecifiedTypeshed;
|
||||
|
||||
/// A builder for a module-resolver test case.
|
||||
///
|
||||
/// The builder takes care of creating a [`TestDb`]
|
||||
/// instance, applying the module resolver settings,
|
||||
/// and creating mock directories for the stdlib, `site-packages`,
|
||||
/// first-party code, etc.
|
||||
///
|
||||
/// For simple tests that do not involve typeshed,
|
||||
/// test cases can be created as follows:
|
||||
///
|
||||
/// ```rs
|
||||
/// let test_case = TestCaseBuilder::new()
|
||||
/// .with_src_files(...)
|
||||
/// .build();
|
||||
///
|
||||
/// let test_case2 = TestCaseBuilder::new()
|
||||
/// .with_site_packages_files(...)
|
||||
/// .build();
|
||||
/// ```
|
||||
///
|
||||
/// Any tests can specify the target Python version that should be used
|
||||
/// in the module resolver settings:
|
||||
///
|
||||
/// ```rs
|
||||
/// let test_case = TestCaseBuilder::new()
|
||||
/// .with_src_files(...)
|
||||
/// .with_target_version(...)
|
||||
/// .build();
|
||||
/// ```
|
||||
///
|
||||
/// For tests checking that standard-library module resolution is working
|
||||
/// correctly, you should usually create a [`MockedTypeshed`] instance
|
||||
/// and pass it to the [`TestCaseBuilder::with_custom_typeshed`] method.
|
||||
/// If you need to check something that involves the vendored typeshed stubs
|
||||
/// we include as part of the binary, you can instead use the
|
||||
/// [`TestCaseBuilder::with_vendored_typeshed`] method.
|
||||
/// For either of these, you should almost always try to be explicit
|
||||
/// about the Python version you want to be specified in the module-resolver
|
||||
/// settings for the test:
|
||||
///
|
||||
/// ```rs
|
||||
/// const TYPESHED = MockedTypeshed { ... };
|
||||
///
|
||||
/// let test_case = resolver_test_case()
|
||||
/// .with_custom_typeshed(TYPESHED)
|
||||
/// .with_target_version(...)
|
||||
/// .build();
|
||||
///
|
||||
/// let test_case2 = resolver_test_case()
|
||||
/// .with_vendored_typeshed()
|
||||
/// .with_target_version(...)
|
||||
/// .build();
|
||||
/// ```
|
||||
///
|
||||
/// If you have not called one of those options, the `stdlib` field
|
||||
/// on the [`TestCase`] instance created from `.build()` will be set
|
||||
/// to `()`.
|
||||
pub(crate) struct TestCaseBuilder<T> {
|
||||
typeshed_option: T,
|
||||
target_version: TargetVersion,
|
||||
first_party_files: Vec<FileSpec>,
|
||||
site_packages_files: Vec<FileSpec>,
|
||||
}
|
||||
|
||||
impl<T> TestCaseBuilder<T> {
|
||||
/// Specify files to be created in the `src` mock directory
|
||||
pub(crate) fn with_src_files(mut self, files: &[FileSpec]) -> Self {
|
||||
self.first_party_files.extend(files.iter().copied());
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify files to be created in the `site-packages` mock directory
|
||||
pub(crate) fn with_site_packages_files(mut self, files: &[FileSpec]) -> Self {
|
||||
self.site_packages_files.extend(files.iter().copied());
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify the target Python version the module resolver should assume
|
||||
pub(crate) fn with_target_version(mut self, target_version: TargetVersion) -> Self {
|
||||
self.target_version = target_version;
|
||||
self
|
||||
}
|
||||
|
||||
fn write_mock_directory(
|
||||
db: &mut TestDb,
|
||||
location: impl AsRef<SystemPath>,
|
||||
files: impl IntoIterator<Item = FileSpec>,
|
||||
) -> SystemPathBuf {
|
||||
let root = location.as_ref().to_path_buf();
|
||||
db.write_files(
|
||||
files
|
||||
.into_iter()
|
||||
.map(|(relative_path, contents)| (root.join(relative_path), contents)),
|
||||
)
|
||||
.unwrap();
|
||||
root
|
||||
}
|
||||
}
|
||||
|
||||
impl TestCaseBuilder<UnspecifiedTypeshed> {
|
||||
pub(crate) fn new() -> TestCaseBuilder<UnspecifiedTypeshed> {
|
||||
Self {
|
||||
typeshed_option: UnspecifiedTypeshed,
|
||||
target_version: TargetVersion::default(),
|
||||
first_party_files: vec![],
|
||||
site_packages_files: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Use the vendored stdlib stubs included in the Ruff binary for this test case
|
||||
pub(crate) fn with_vendored_typeshed(self) -> TestCaseBuilder<VendoredTypeshed> {
|
||||
let TestCaseBuilder {
|
||||
typeshed_option: _,
|
||||
target_version,
|
||||
first_party_files,
|
||||
site_packages_files,
|
||||
} = self;
|
||||
TestCaseBuilder {
|
||||
typeshed_option: VendoredTypeshed,
|
||||
target_version,
|
||||
first_party_files,
|
||||
site_packages_files,
|
||||
}
|
||||
}
|
||||
|
||||
/// Use a mock typeshed directory for this test case
|
||||
pub(crate) fn with_custom_typeshed(
|
||||
self,
|
||||
typeshed: MockedTypeshed,
|
||||
) -> TestCaseBuilder<MockedTypeshed> {
|
||||
let TestCaseBuilder {
|
||||
typeshed_option: _,
|
||||
target_version,
|
||||
first_party_files,
|
||||
site_packages_files,
|
||||
} = self;
|
||||
TestCaseBuilder {
|
||||
typeshed_option: typeshed,
|
||||
target_version,
|
||||
first_party_files,
|
||||
site_packages_files,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build(self) -> TestCase<()> {
|
||||
let TestCase {
|
||||
db,
|
||||
src,
|
||||
stdlib: _,
|
||||
site_packages,
|
||||
target_version,
|
||||
} = self.with_custom_typeshed(MockedTypeshed::default()).build();
|
||||
TestCase {
|
||||
db,
|
||||
src,
|
||||
stdlib: (),
|
||||
site_packages,
|
||||
target_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TestCaseBuilder<MockedTypeshed> {
|
||||
pub(crate) fn build(self) -> TestCase<SystemPathBuf> {
|
||||
let TestCaseBuilder {
|
||||
typeshed_option,
|
||||
target_version,
|
||||
first_party_files,
|
||||
site_packages_files,
|
||||
} = self;
|
||||
|
||||
let mut db = TestDb::new();
|
||||
|
||||
let site_packages =
|
||||
Self::write_mock_directory(&mut db, "/site-packages", site_packages_files);
|
||||
let src = Self::write_mock_directory(&mut db, "/src", first_party_files);
|
||||
let typeshed = Self::build_typeshed_mock(&mut db, &typeshed_option);
|
||||
|
||||
set_module_resolution_settings(
|
||||
&mut db,
|
||||
RawModuleResolutionSettings {
|
||||
target_version,
|
||||
extra_paths: vec![],
|
||||
workspace_root: src.clone(),
|
||||
custom_typeshed: Some(typeshed.clone()),
|
||||
site_packages: Some(site_packages.clone()),
|
||||
},
|
||||
);
|
||||
|
||||
TestCase {
|
||||
db,
|
||||
src,
|
||||
stdlib: typeshed.join("stdlib"),
|
||||
site_packages,
|
||||
target_version,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_typeshed_mock(db: &mut TestDb, typeshed_to_build: &MockedTypeshed) -> SystemPathBuf {
|
||||
let typeshed = SystemPathBuf::from("/typeshed");
|
||||
let MockedTypeshed {
|
||||
stdlib_files,
|
||||
versions,
|
||||
} = typeshed_to_build;
|
||||
Self::write_mock_directory(
|
||||
db,
|
||||
typeshed.join("stdlib"),
|
||||
stdlib_files
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(std::iter::once(("VERSIONS", *versions))),
|
||||
);
|
||||
typeshed
|
||||
}
|
||||
}
|
||||
|
||||
impl TestCaseBuilder<VendoredTypeshed> {
|
||||
pub(crate) fn build(self) -> TestCase<VendoredPathBuf> {
|
||||
let TestCaseBuilder {
|
||||
typeshed_option: VendoredTypeshed,
|
||||
target_version,
|
||||
first_party_files,
|
||||
site_packages_files,
|
||||
} = self;
|
||||
|
||||
let mut db = TestDb::new();
|
||||
|
||||
let site_packages =
|
||||
Self::write_mock_directory(&mut db, "/site-packages", site_packages_files);
|
||||
let src = Self::write_mock_directory(&mut db, "/src", first_party_files);
|
||||
|
||||
set_module_resolution_settings(
|
||||
&mut db,
|
||||
RawModuleResolutionSettings {
|
||||
target_version,
|
||||
extra_paths: vec![],
|
||||
workspace_root: src.clone(),
|
||||
custom_typeshed: None,
|
||||
site_packages: Some(site_packages.clone()),
|
||||
},
|
||||
);
|
||||
|
||||
TestCase {
|
||||
db,
|
||||
src,
|
||||
stdlib: VendoredPathBuf::from("stdlib"),
|
||||
site_packages,
|
||||
target_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
pub use self::vendored::vendored_typeshed_stubs;
|
||||
pub(crate) use self::versions::{
|
||||
parse_typeshed_versions, LazyTypeshedVersions, TypeshedVersionsQueryResult,
|
||||
};
|
||||
pub use self::versions::{TypeshedVersionsParseError, TypeshedVersionsParseErrorKind};
|
||||
|
||||
mod vendored;
|
||||
mod versions;
|
||||
@@ -1,99 +0,0 @@
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
|
||||
// The file path here is hardcoded in this crate's `build.rs` script.
|
||||
// Luckily this crate will fail to build if this file isn't available at build time.
|
||||
static TYPESHED_ZIP_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/zipped_typeshed.zip"));
|
||||
|
||||
pub fn vendored_typeshed_stubs() -> &'static VendoredFileSystem {
|
||||
static VENDORED_TYPESHED_STUBS: Lazy<VendoredFileSystem> =
|
||||
Lazy::new(|| VendoredFileSystem::new_static(TYPESHED_ZIP_BYTES).unwrap());
|
||||
&VENDORED_TYPESHED_STUBS
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::{self, Read};
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_db::vendored::VendoredPath;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn typeshed_zip_created_at_build_time() {
|
||||
let mut typeshed_zip_archive =
|
||||
zip::ZipArchive::new(io::Cursor::new(TYPESHED_ZIP_BYTES)).unwrap();
|
||||
|
||||
let mut functools_module_stub = typeshed_zip_archive
|
||||
.by_name("stdlib/functools.pyi")
|
||||
.unwrap();
|
||||
assert!(functools_module_stub.is_file());
|
||||
|
||||
let mut functools_module_stub_source = String::new();
|
||||
functools_module_stub
|
||||
.read_to_string(&mut functools_module_stub_source)
|
||||
.unwrap();
|
||||
|
||||
assert!(functools_module_stub_source.contains("def update_wrapper("));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typeshed_vfs_consistent_with_vendored_stubs() {
|
||||
let vendored_typeshed_dir = Path::new("vendor/typeshed").canonicalize().unwrap();
|
||||
let vendored_typeshed_stubs = vendored_typeshed_stubs();
|
||||
|
||||
let mut empty_iterator = true;
|
||||
for entry in walkdir::WalkDir::new(&vendored_typeshed_dir).min_depth(1) {
|
||||
empty_iterator = false;
|
||||
let entry = entry.unwrap();
|
||||
let absolute_path = entry.path();
|
||||
let file_type = entry.file_type();
|
||||
|
||||
let relative_path = absolute_path
|
||||
.strip_prefix(&vendored_typeshed_dir)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("Expected {absolute_path:?} to be a child of {vendored_typeshed_dir:?}")
|
||||
});
|
||||
|
||||
let vendored_path = <&VendoredPath>::try_from(relative_path)
|
||||
.unwrap_or_else(|_| panic!("Expected {relative_path:?} to be valid UTF-8"));
|
||||
|
||||
assert!(
|
||||
vendored_typeshed_stubs.exists(vendored_path),
|
||||
"Expected {vendored_path:?} to exist in the `VendoredFileSystem`!
|
||||
|
||||
Vendored file system:
|
||||
|
||||
{vendored_typeshed_stubs:#?}
|
||||
"
|
||||
);
|
||||
|
||||
let vendored_path_kind = vendored_typeshed_stubs
|
||||
.metadata(vendored_path)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Expected metadata for {vendored_path:?} to be retrievable from the `VendoredFileSystem!
|
||||
|
||||
Vendored file system:
|
||||
|
||||
{vendored_typeshed_stubs:#?}
|
||||
"
|
||||
)
|
||||
})
|
||||
.kind();
|
||||
|
||||
assert_eq!(
|
||||
vendored_path_kind.is_directory(),
|
||||
file_type.is_dir(),
|
||||
"{vendored_path:?} had type {vendored_path_kind:?}, inconsistent with fs path {relative_path:?}: {file_type:?}"
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
!empty_iterator,
|
||||
"Expected there to be at least one file or directory in the vendored typeshed stubs!"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,814 +0,0 @@
|
||||
use std::cell::OnceCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::num::{NonZeroU16, NonZeroUsize};
|
||||
use std::ops::{RangeFrom, RangeInclusive};
|
||||
use std::str::FromStr;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use ruff_db::system::SystemPath;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_db::files::{system_path_to_file, File};
|
||||
use ruff_db::source::source_text;
|
||||
|
||||
use crate::db::Db;
|
||||
use crate::module_name::ModuleName;
|
||||
use crate::supported_py_version::TargetVersion;
|
||||
|
||||
use super::vendored::vendored_typeshed_stubs;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LazyTypeshedVersions<'db>(OnceCell<&'db TypeshedVersions>);
|
||||
|
||||
impl<'db> LazyTypeshedVersions<'db> {
|
||||
#[must_use]
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(OnceCell::new())
|
||||
}
|
||||
|
||||
/// Query whether a module exists at runtime in the stdlib on a certain Python version.
|
||||
///
|
||||
/// Simply probing whether a file exists in typeshed is insufficient for this question,
|
||||
/// as a module in the stdlib may have been added in Python 3.10, but the typeshed stub
|
||||
/// will still be available (either in a custom typeshed dir or in our vendored copy)
|
||||
/// even if the user specified Python 3.8 as the target version.
|
||||
///
|
||||
/// For top-level modules and packages, the VERSIONS file can always provide an unambiguous answer
|
||||
/// as to whether the module exists on the specified target version. However, VERSIONS does not
|
||||
/// provide comprehensive information on all submodules, meaning that this method sometimes
|
||||
/// returns [`TypeshedVersionsQueryResult::MaybeExists`].
|
||||
/// See [`TypeshedVersionsQueryResult`] for more details.
|
||||
#[must_use]
|
||||
pub(crate) fn query_module(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
module: &ModuleName,
|
||||
stdlib_root: Option<&SystemPath>,
|
||||
target_version: TargetVersion,
|
||||
) -> TypeshedVersionsQueryResult {
|
||||
let versions = self.0.get_or_init(|| {
|
||||
let versions_path = if let Some(system_path) = stdlib_root {
|
||||
system_path.join("VERSIONS")
|
||||
} else {
|
||||
return &VENDORED_VERSIONS;
|
||||
};
|
||||
let Some(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}"
|
||||
)
|
||||
};
|
||||
// TODO(Alex/Micha): If VERSIONS is invalid,
|
||||
// this should invalidate not just the specific module resolution we're currently attempting,
|
||||
// but all type inference that depends on any standard-library types.
|
||||
// Unwrapping here is not correct...
|
||||
parse_typeshed_versions(db, versions_file).as_ref().unwrap()
|
||||
});
|
||||
versions.query_module(module, PyVersion::from(target_version))
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(return_ref)]
|
||||
pub(crate) fn parse_typeshed_versions(
|
||||
db: &dyn Db,
|
||||
versions_file: File,
|
||||
) -> Result<TypeshedVersions, TypeshedVersionsParseError> {
|
||||
let file_content = source_text(db.upcast(), versions_file);
|
||||
file_content.parse()
|
||||
}
|
||||
|
||||
static VENDORED_VERSIONS: Lazy<TypeshedVersions> = Lazy::new(|| {
|
||||
TypeshedVersions::from_str(
|
||||
&vendored_typeshed_stubs()
|
||||
.read_to_string("stdlib/VERSIONS")
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct TypeshedVersionsParseError {
|
||||
line_number: Option<NonZeroU16>,
|
||||
reason: TypeshedVersionsParseErrorKind,
|
||||
}
|
||||
|
||||
impl fmt::Display for TypeshedVersionsParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let TypeshedVersionsParseError {
|
||||
line_number,
|
||||
reason,
|
||||
} = self;
|
||||
if let Some(line_number) = line_number {
|
||||
write!(
|
||||
f,
|
||||
"Error while parsing line {line_number} of typeshed's VERSIONS file: {reason}"
|
||||
)
|
||||
} else {
|
||||
write!(f, "Error while parsing typeshed's VERSIONS file: {reason}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for TypeshedVersionsParseError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
if let TypeshedVersionsParseErrorKind::IntegerParsingFailure { err, .. } = &self.reason {
|
||||
Some(err)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum TypeshedVersionsParseErrorKind {
|
||||
TooManyLines(NonZeroUsize),
|
||||
UnexpectedNumberOfColons,
|
||||
InvalidModuleName(String),
|
||||
UnexpectedNumberOfHyphens,
|
||||
UnexpectedNumberOfPeriods(String),
|
||||
IntegerParsingFailure {
|
||||
version: String,
|
||||
err: std::num::ParseIntError,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for TypeshedVersionsParseErrorKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::TooManyLines(num_lines) => write!(
|
||||
f,
|
||||
"File has too many lines ({num_lines}); maximum allowed is {}",
|
||||
NonZeroU16::MAX
|
||||
),
|
||||
Self::UnexpectedNumberOfColons => {
|
||||
f.write_str("Expected every non-comment line to have exactly one colon")
|
||||
}
|
||||
Self::InvalidModuleName(name) => write!(
|
||||
f,
|
||||
"Expected all components of '{name}' to be valid Python identifiers"
|
||||
),
|
||||
Self::UnexpectedNumberOfHyphens => {
|
||||
f.write_str("Expected every non-comment line to have exactly one '-' character")
|
||||
}
|
||||
Self::UnexpectedNumberOfPeriods(format) => write!(
|
||||
f,
|
||||
"Expected all versions to be in the form {{MAJOR}}.{{MINOR}}; got '{format}'"
|
||||
),
|
||||
Self::IntegerParsingFailure { version, err } => write!(
|
||||
f,
|
||||
"Failed to convert '{version}' to a pair of integers due to {err}",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct TypeshedVersions(FxHashMap<ModuleName, PyVersionRange>);
|
||||
|
||||
impl TypeshedVersions {
|
||||
#[must_use]
|
||||
fn exact(&self, module_name: &ModuleName) -> Option<&PyVersionRange> {
|
||||
self.0.get(module_name)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn query_module(
|
||||
&self,
|
||||
module: &ModuleName,
|
||||
target_version: PyVersion,
|
||||
) -> TypeshedVersionsQueryResult {
|
||||
if let Some(range) = self.exact(module) {
|
||||
if range.contains(target_version) {
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
} else {
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
}
|
||||
} else {
|
||||
let mut module = module.parent();
|
||||
while let Some(module_to_try) = module {
|
||||
if let Some(range) = self.exact(&module_to_try) {
|
||||
return {
|
||||
if range.contains(target_version) {
|
||||
TypeshedVersionsQueryResult::MaybeExists
|
||||
} else {
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
}
|
||||
};
|
||||
}
|
||||
module = module_to_try.parent();
|
||||
}
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible answers [`LazyTypeshedVersions::query_module()`] could give to the question:
|
||||
/// "Does this module exist in the stdlib at runtime on a certain target version?"
|
||||
#[derive(Debug, Copy, PartialEq, Eq, Clone, Hash)]
|
||||
pub(crate) enum TypeshedVersionsQueryResult {
|
||||
/// The module definitely exists in the stdlib at runtime on the user-specified target version.
|
||||
///
|
||||
/// For example:
|
||||
/// - The target version is Python 3.8
|
||||
/// - We're querying whether the `asyncio.tasks` module exists in the stdlib
|
||||
/// - The VERSIONS file contains the line `asyncio.tasks: 3.8-`
|
||||
Exists,
|
||||
|
||||
/// The module definitely does not exist in the stdlib on the user-specified target version.
|
||||
///
|
||||
/// For example:
|
||||
/// - We're querying whether the `foo` module exists in the stdlib
|
||||
/// - There is no top-level `foo` module in VERSIONS
|
||||
///
|
||||
/// OR:
|
||||
/// - The target version is Python 3.8
|
||||
/// - We're querying whether the module `importlib.abc` exists in the stdlib
|
||||
/// - The VERSIONS file contains the line `importlib.abc: 3.10-`,
|
||||
/// indicating that the module was added in 3.10
|
||||
///
|
||||
/// OR:
|
||||
/// - The target version is Python 3.8
|
||||
/// - We're querying whether the module `collections.abc` exists in the stdlib
|
||||
/// - The VERSIONS file does not contain any information about the `collections.abc` submodule,
|
||||
/// but *does* contain the line `collections: 3.10-`,
|
||||
/// indicating that the entire `collections` package was added in Python 3.10.
|
||||
DoesNotExist,
|
||||
|
||||
/// The module potentially exists in the stdlib and, if it does,
|
||||
/// it definitely exists on the user-specified target version.
|
||||
///
|
||||
/// This variant is only relevant for submodules,
|
||||
/// for which the typeshed VERSIONS file does not provide comprehensive information.
|
||||
/// (The VERSIONS file is guaranteed to provide information about all top-level stdlib modules and packages,
|
||||
/// but not necessarily about all submodules within each top-level package.)
|
||||
///
|
||||
/// For example:
|
||||
/// - The target version is Python 3.8
|
||||
/// - We're querying whether the `asyncio.staggered` module exists in the stdlib
|
||||
/// - The typeshed VERSIONS file contains the line `asyncio: 3.8`,
|
||||
/// indicating that the `asyncio` package was added in Python 3.8,
|
||||
/// but does not contain any explicit information about the `asyncio.staggered` submodule.
|
||||
MaybeExists,
|
||||
}
|
||||
|
||||
impl FromStr for TypeshedVersions {
|
||||
type Err = TypeshedVersionsParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut map = FxHashMap::default();
|
||||
|
||||
for (line_index, line) in s.lines().enumerate() {
|
||||
// humans expect line numbers to be 1-indexed
|
||||
let line_number = NonZeroUsize::new(line_index.saturating_add(1)).unwrap();
|
||||
|
||||
let Ok(line_number) = NonZeroU16::try_from(line_number) else {
|
||||
return Err(TypeshedVersionsParseError {
|
||||
line_number: None,
|
||||
reason: TypeshedVersionsParseErrorKind::TooManyLines(line_number),
|
||||
});
|
||||
};
|
||||
|
||||
let Some(content) = line.split('#').map(str::trim).next() else {
|
||||
continue;
|
||||
};
|
||||
if content.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut parts = content.split(':').map(str::trim);
|
||||
let (Some(module_name), Some(rest), None) = (parts.next(), parts.next(), parts.next())
|
||||
else {
|
||||
return Err(TypeshedVersionsParseError {
|
||||
line_number: Some(line_number),
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfColons,
|
||||
});
|
||||
};
|
||||
|
||||
let Some(module_name) = ModuleName::new(module_name) else {
|
||||
return Err(TypeshedVersionsParseError {
|
||||
line_number: Some(line_number),
|
||||
reason: TypeshedVersionsParseErrorKind::InvalidModuleName(
|
||||
module_name.to_string(),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
match PyVersionRange::from_str(rest) {
|
||||
Ok(version) => map.insert(module_name, version),
|
||||
Err(reason) => {
|
||||
return Err(TypeshedVersionsParseError {
|
||||
line_number: Some(line_number),
|
||||
reason,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Self(map))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypeshedVersions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let sorted_items: BTreeMap<&ModuleName, &PyVersionRange> = self.0.iter().collect();
|
||||
for (module_name, range) in sorted_items {
|
||||
writeln!(f, "{module_name}: {range}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
enum PyVersionRange {
|
||||
AvailableFrom(RangeFrom<PyVersion>),
|
||||
AvailableWithin(RangeInclusive<PyVersion>),
|
||||
}
|
||||
|
||||
impl PyVersionRange {
|
||||
#[must_use]
|
||||
fn contains(&self, version: PyVersion) -> bool {
|
||||
match self {
|
||||
Self::AvailableFrom(inner) => inner.contains(&version),
|
||||
Self::AvailableWithin(inner) => inner.contains(&version),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PyVersionRange {
|
||||
type Err = TypeshedVersionsParseErrorKind;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut parts = s.split('-').map(str::trim);
|
||||
match (parts.next(), parts.next(), parts.next()) {
|
||||
(Some(lower), Some(""), None) => Ok(Self::AvailableFrom((lower.parse()?)..)),
|
||||
(Some(lower), Some(upper), None) => {
|
||||
Ok(Self::AvailableWithin((lower.parse()?)..=(upper.parse()?)))
|
||||
}
|
||||
_ => Err(TypeshedVersionsParseErrorKind::UnexpectedNumberOfHyphens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PyVersionRange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::AvailableFrom(range_from) => write!(f, "{}-", range_from.start),
|
||||
Self::AvailableWithin(range_inclusive) => {
|
||||
write!(f, "{}-{}", range_inclusive.start(), range_inclusive.end())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
struct PyVersion {
|
||||
major: u8,
|
||||
minor: u8,
|
||||
}
|
||||
|
||||
impl FromStr for PyVersion {
|
||||
type Err = TypeshedVersionsParseErrorKind;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut parts = s.split('.').map(str::trim);
|
||||
let (Some(major), Some(minor), None) = (parts.next(), parts.next(), parts.next()) else {
|
||||
return Err(TypeshedVersionsParseErrorKind::UnexpectedNumberOfPeriods(
|
||||
s.to_string(),
|
||||
));
|
||||
};
|
||||
let major = match u8::from_str(major) {
|
||||
Ok(major) => major,
|
||||
Err(err) => {
|
||||
return Err(TypeshedVersionsParseErrorKind::IntegerParsingFailure {
|
||||
version: s.to_string(),
|
||||
err,
|
||||
})
|
||||
}
|
||||
};
|
||||
let minor = match u8::from_str(minor) {
|
||||
Ok(minor) => minor,
|
||||
Err(err) => {
|
||||
return Err(TypeshedVersionsParseErrorKind::IntegerParsingFailure {
|
||||
version: s.to_string(),
|
||||
err,
|
||||
})
|
||||
}
|
||||
};
|
||||
Ok(Self { major, minor })
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PyVersion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let PyVersion { major, minor } = self;
|
||||
write!(f, "{major}.{minor}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetVersion> for PyVersion {
|
||||
fn from(value: TargetVersion) -> Self {
|
||||
match value {
|
||||
TargetVersion::Py37 => PyVersion { major: 3, minor: 7 },
|
||||
TargetVersion::Py38 => PyVersion { major: 3, minor: 8 },
|
||||
TargetVersion::Py39 => PyVersion { major: 3, minor: 9 },
|
||||
TargetVersion::Py310 => PyVersion {
|
||||
major: 3,
|
||||
minor: 10,
|
||||
},
|
||||
TargetVersion::Py311 => PyVersion {
|
||||
major: 3,
|
||||
minor: 11,
|
||||
},
|
||||
TargetVersion::Py312 => PyVersion {
|
||||
major: 3,
|
||||
minor: 12,
|
||||
},
|
||||
TargetVersion::Py313 => PyVersion {
|
||||
major: 3,
|
||||
minor: 13,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::num::{IntErrorKind, NonZeroU16};
|
||||
use std::path::Path;
|
||||
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use super::*;
|
||||
|
||||
const TYPESHED_STDLIB_DIR: &str = "stdlib";
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
const ONE: Option<NonZeroU16> = Some(unsafe { NonZeroU16::new_unchecked(1) });
|
||||
|
||||
impl TypeshedVersions {
|
||||
#[must_use]
|
||||
fn contains_exact(&self, module: &ModuleName) -> bool {
|
||||
self.exact(module).is_some()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_vendored_versions_file() {
|
||||
let versions_data = include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/vendor/typeshed/stdlib/VERSIONS"
|
||||
));
|
||||
|
||||
let versions = TypeshedVersions::from_str(versions_data).unwrap();
|
||||
assert!(versions.len() > 100);
|
||||
assert!(versions.len() < 1000);
|
||||
|
||||
let asyncio = ModuleName::new_static("asyncio").unwrap();
|
||||
let asyncio_staggered = ModuleName::new_static("asyncio.staggered").unwrap();
|
||||
let audioop = ModuleName::new_static("audioop").unwrap();
|
||||
|
||||
assert!(versions.contains_exact(&asyncio));
|
||||
assert_eq!(
|
||||
versions.query_module(&asyncio, TargetVersion::Py310.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
|
||||
assert!(versions.contains_exact(&asyncio_staggered));
|
||||
assert_eq!(
|
||||
versions.query_module(&asyncio_staggered, TargetVersion::Py38.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
assert_eq!(
|
||||
versions.query_module(&asyncio_staggered, TargetVersion::Py37.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
|
||||
assert!(versions.contains_exact(&audioop));
|
||||
assert_eq!(
|
||||
versions.query_module(&audioop, TargetVersion::Py312.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
assert_eq!(
|
||||
versions.query_module(&audioop, TargetVersion::Py313.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typeshed_versions_consistent_with_vendored_stubs() {
|
||||
const VERSIONS_DATA: &str = include_str!("../../vendor/typeshed/stdlib/VERSIONS");
|
||||
let vendored_typeshed_dir = Path::new("vendor/typeshed").canonicalize().unwrap();
|
||||
let vendored_typeshed_versions = TypeshedVersions::from_str(VERSIONS_DATA).unwrap();
|
||||
|
||||
let mut empty_iterator = true;
|
||||
|
||||
let stdlib_stubs_path = vendored_typeshed_dir.join(TYPESHED_STDLIB_DIR);
|
||||
|
||||
for entry in std::fs::read_dir(&stdlib_stubs_path).unwrap() {
|
||||
empty_iterator = false;
|
||||
let entry = entry.unwrap();
|
||||
let absolute_path = entry.path();
|
||||
|
||||
let relative_path = absolute_path
|
||||
.strip_prefix(&stdlib_stubs_path)
|
||||
.unwrap_or_else(|_| panic!("Expected path to be a child of {stdlib_stubs_path:?} but found {absolute_path:?}"));
|
||||
|
||||
let relative_path_str = relative_path.as_os_str().to_str().unwrap_or_else(|| {
|
||||
panic!("Expected all typeshed paths to be valid UTF-8; got {relative_path:?}")
|
||||
});
|
||||
if relative_path_str == "VERSIONS" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let top_level_module = if let Some(extension) = relative_path.extension() {
|
||||
// It was a file; strip off the file extension to get the module name:
|
||||
let extension = extension
|
||||
.to_str()
|
||||
.unwrap_or_else(||panic!("Expected all file extensions to be UTF-8; was not true for {relative_path:?}"));
|
||||
|
||||
relative_path_str
|
||||
.strip_suffix(extension)
|
||||
.and_then(|string| string.strip_suffix('.')).unwrap_or_else(|| {
|
||||
panic!("Expected path {relative_path_str:?} to end with computed extension {extension:?}")
|
||||
})
|
||||
} else {
|
||||
// It was a directory; no need to do anything to get the module name
|
||||
relative_path_str
|
||||
};
|
||||
|
||||
let top_level_module = ModuleName::new(top_level_module)
|
||||
.unwrap_or_else(|| panic!("{top_level_module:?} was not a valid module name!"));
|
||||
|
||||
assert!(vendored_typeshed_versions.contains_exact(&top_level_module));
|
||||
}
|
||||
|
||||
assert!(
|
||||
!empty_iterator,
|
||||
"Expected there to be at least one file or directory in the vendored typeshed stubs"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_mock_versions_file() {
|
||||
const VERSIONS: &str = "\
|
||||
# a comment
|
||||
# some more comment
|
||||
# yet more comment
|
||||
|
||||
|
||||
# and some more comment
|
||||
|
||||
bar: 2.7-3.10
|
||||
|
||||
# more comment
|
||||
bar.baz: 3.1-3.9
|
||||
foo: 3.8- # trailing comment
|
||||
";
|
||||
let parsed_versions = TypeshedVersions::from_str(VERSIONS).unwrap();
|
||||
assert_eq!(parsed_versions.len(), 3);
|
||||
assert_snapshot!(parsed_versions.to_string(), @r###"
|
||||
bar: 2.7-3.10
|
||||
bar.baz: 3.1-3.9
|
||||
foo: 3.8-
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_within_range_parsed_correctly() {
|
||||
let parsed_versions = TypeshedVersions::from_str("bar: 2.7-3.10").unwrap();
|
||||
let bar = ModuleName::new_static("bar").unwrap();
|
||||
|
||||
assert!(parsed_versions.contains_exact(&bar));
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar, TargetVersion::Py37.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar, TargetVersion::Py310.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar, TargetVersion::Py311.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_from_range_parsed_correctly() {
|
||||
let parsed_versions = TypeshedVersions::from_str("foo: 3.8-").unwrap();
|
||||
let foo = ModuleName::new_static("foo").unwrap();
|
||||
|
||||
assert!(parsed_versions.contains_exact(&foo));
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&foo, TargetVersion::Py37.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&foo, TargetVersion::Py38.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&foo, TargetVersion::Py311.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_submodule_parsed_correctly() {
|
||||
let parsed_versions = TypeshedVersions::from_str("bar.baz: 3.1-3.9").unwrap();
|
||||
let bar_baz = ModuleName::new_static("bar.baz").unwrap();
|
||||
|
||||
assert!(parsed_versions.contains_exact(&bar_baz));
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar_baz, TargetVersion::Py37.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar_baz, TargetVersion::Py39.into()),
|
||||
TypeshedVersionsQueryResult::Exists
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar_baz, TargetVersion::Py310.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_submodule_queried_correctly() {
|
||||
let parsed_versions = TypeshedVersions::from_str("bar: 2.7-3.10").unwrap();
|
||||
let bar_eggs = ModuleName::new_static("bar.eggs").unwrap();
|
||||
|
||||
assert!(!parsed_versions.contains_exact(&bar_eggs));
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar_eggs, TargetVersion::Py37.into()),
|
||||
TypeshedVersionsQueryResult::MaybeExists
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar_eggs, TargetVersion::Py310.into()),
|
||||
TypeshedVersionsQueryResult::MaybeExists
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&bar_eggs, TargetVersion::Py311.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonexistent_module_queried_correctly() {
|
||||
let parsed_versions = TypeshedVersions::from_str("eggs: 3.8-").unwrap();
|
||||
let spam = ModuleName::new_static("spam").unwrap();
|
||||
|
||||
assert!(!parsed_versions.contains_exact(&spam));
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&spam, TargetVersion::Py37.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
assert_eq!(
|
||||
parsed_versions.query_module(&spam, TargetVersion::Py313.into()),
|
||||
TypeshedVersionsQueryResult::DoesNotExist
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_huge_versions_file() {
|
||||
let offset = 100;
|
||||
let too_many = u16::MAX as usize + offset;
|
||||
|
||||
let mut massive_versions_file = String::new();
|
||||
for i in 0..too_many {
|
||||
massive_versions_file.push_str(&format!("x{i}: 3.8-\n"));
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str(&massive_versions_file),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: None,
|
||||
reason: TypeshedVersionsParseErrorKind::TooManyLines(
|
||||
NonZeroUsize::new(too_many + 1 - offset).unwrap()
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_typeshed_versions_bad_colon_number() {
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo 3.7"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfColons
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo:: 3.7"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfColons
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_typeshed_versions_non_identifier_modules() {
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("not!an!identifier!: 3.7"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::InvalidModuleName(
|
||||
"not!an!identifier!".to_string()
|
||||
)
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("(also_not).(an_identifier): 3.7"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::InvalidModuleName(
|
||||
"(also_not).(an_identifier)".to_string()
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_typeshed_versions_bad_hyphen_number() {
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo: 3.8"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfHyphens
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo: 3.8--"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfHyphens
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo: 3.8--3.9"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfHyphens
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_typeshed_versions_bad_period_number() {
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo: 38-"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfPeriods("38".to_string())
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo: 3..8-"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfPeriods(
|
||||
"3..8".to_string()
|
||||
)
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
TypeshedVersions::from_str("foo: 3.8-3..11"),
|
||||
Err(TypeshedVersionsParseError {
|
||||
line_number: ONE,
|
||||
reason: TypeshedVersionsParseErrorKind::UnexpectedNumberOfPeriods(
|
||||
"3..11".to_string()
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_typeshed_versions_non_digits() {
|
||||
let err = TypeshedVersions::from_str("foo: 1.two-").unwrap_err();
|
||||
assert_eq!(err.line_number, ONE);
|
||||
let TypeshedVersionsParseErrorKind::IntegerParsingFailure { version, err } = err.reason
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(version, "1.two".to_string());
|
||||
assert_eq!(*err.kind(), IntErrorKind::InvalidDigit);
|
||||
|
||||
let err = TypeshedVersions::from_str("foo: 3.8-four.9").unwrap_err();
|
||||
assert_eq!(err.line_number, ONE);
|
||||
let TypeshedVersionsParseErrorKind::IntegerParsingFailure { version, err } = err.reason
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(version, "four.9".to_string());
|
||||
assert_eq!(*err.kind(), IntErrorKind::InvalidDigit);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
The "typeshed" project is licensed under the terms of the Apache license, as
|
||||
reproduced below.
|
||||
|
||||
= = = = =
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
= = = = =
|
||||
|
||||
Parts of typeshed are licensed under different licenses (like the MIT
|
||||
license), reproduced below.
|
||||
|
||||
= = = = =
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2015 Jukka Lehtosalo and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
= = = = =
|
||||
@@ -1,124 +0,0 @@
|
||||
# typeshed
|
||||
|
||||
[](https://github.com/python/typeshed/actions/workflows/tests.yml)
|
||||
[](https://gitter.im/python/typing?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://github.com/python/typeshed/blob/main/CONTRIBUTING.md)
|
||||
|
||||
## About
|
||||
|
||||
Typeshed contains external type annotations for the Python standard library
|
||||
and Python builtins, as well as third party packages as contributed by
|
||||
people external to those projects.
|
||||
|
||||
This data can e.g. be used for static analysis, type checking, type inference,
|
||||
and autocompletion.
|
||||
|
||||
For information on how to use typeshed, read below. Information for
|
||||
contributors can be found in [CONTRIBUTING.md](CONTRIBUTING.md). **Please read
|
||||
it before submitting pull requests; do not report issues with annotations to
|
||||
the project the stubs are for, but instead report them here to typeshed.**
|
||||
|
||||
Further documentation on stub files, typeshed, and Python's typing system in
|
||||
general, can also be found at https://typing.readthedocs.io/en/latest/.
|
||||
|
||||
Typeshed supports Python versions 3.8 and up.
|
||||
|
||||
## Using
|
||||
|
||||
If you're just using a type checker ([mypy](https://github.com/python/mypy/),
|
||||
[pyright](https://github.com/microsoft/pyright),
|
||||
[pytype](https://github.com/google/pytype/), PyCharm, ...), as opposed to
|
||||
developing it, you don't need to interact with the typeshed repo at
|
||||
all: a copy of standard library part of typeshed is bundled with type checkers.
|
||||
And type stubs for third party packages and modules you are using can
|
||||
be installed from PyPI. For example, if you are using `html5lib` and `requests`,
|
||||
you can install the type stubs using
|
||||
|
||||
```bash
|
||||
$ pip install types-html5lib types-requests
|
||||
```
|
||||
|
||||
These PyPI packages follow [PEP 561](http://www.python.org/dev/peps/pep-0561/)
|
||||
and are automatically released (up to once a day) by
|
||||
[typeshed internal machinery](https://github.com/typeshed-internal/stub_uploader).
|
||||
|
||||
Type checkers should be able to use these stub packages when installed. For more
|
||||
details, see the documentation for your type checker.
|
||||
|
||||
### Package versioning for third-party stubs
|
||||
|
||||
Version numbers of third-party stub packages consist of at least four parts.
|
||||
All parts of the stub version, except for the last part, correspond to the
|
||||
version of the runtime package being stubbed. For example, if the `types-foo`
|
||||
package has version `1.2.0.20240309`, this guarantees that the `types-foo` package
|
||||
contains stubs targeted against `foo==1.2.*` and tested against the latest
|
||||
version of `foo` matching that specifier. In this example, the final element
|
||||
of the version number (20240309) indicates that the stub package was pushed on
|
||||
March 9, 2024.
|
||||
|
||||
At typeshed, we try to keep breaking changes to a minimum. However, due to the
|
||||
nature of stubs, any version bump can introduce changes that might make your
|
||||
code fail to type check.
|
||||
|
||||
There are several strategies available for specifying the version of a stubs
|
||||
package you're using, each with its own tradeoffs:
|
||||
|
||||
1. Use the same bounds that you use for the package being stubbed. For example,
|
||||
if you use `requests>=2.30.0,<2.32`, you can use
|
||||
`types-requests>=2.30.0,<2.32`. This ensures that the stubs are compatible
|
||||
with the package you are using, but it carries a small risk of breaking
|
||||
type checking due to changes in the stubs.
|
||||
|
||||
Another risk of this strategy is that stubs often lag behind
|
||||
the package being stubbed. You might want to force the package being stubbed
|
||||
to a certain minimum version because it fixes a critical bug, but if
|
||||
correspondingly updated stubs have not been released, your type
|
||||
checking results may not be fully accurate.
|
||||
2. Pin the stubs to a known good version and update the pin from time to time
|
||||
(either manually, or using a tool such as dependabot or renovate).
|
||||
|
||||
For example, if you use `types-requests==2.31.0.1`, you can have confidence
|
||||
that upgrading dependencies will not break type checking. However, you will
|
||||
miss out on improvements in the stubs that could potentially improve type
|
||||
checking until you update the pin. This strategy also has the risk that the
|
||||
stubs you are using might become incompatible with the package being stubbed.
|
||||
3. Don't pin the stubs. This is the option that demands the least work from
|
||||
you when it comes to updating version pins, and has the advantage that you
|
||||
will automatically benefit from improved stubs whenever a new version of the
|
||||
stubs package is released. However, it carries the risk that the stubs
|
||||
become incompatible with the package being stubbed.
|
||||
|
||||
For example, if a new major version of the package is released, there's a
|
||||
chance the stubs might be updated to reflect the new version of the runtime
|
||||
package before you update the package being stubbed.
|
||||
|
||||
You can also switch between the different strategies as needed. For example,
|
||||
you could default to strategy (1), but fall back to strategy (2) when
|
||||
a problem arises that can't easily be fixed.
|
||||
|
||||
### The `_typeshed` package
|
||||
|
||||
typeshed includes a package `_typeshed` as part of the standard library.
|
||||
This package and its submodules contain utility types, but are not
|
||||
available at runtime. For more information about how to use this package,
|
||||
[see the `stdlib/_typeshed` directory](https://github.com/python/typeshed/tree/main/stdlib/_typeshed).
|
||||
|
||||
## Discussion
|
||||
|
||||
If you've run into behavior in the type checker that suggests the type
|
||||
stubs for a given library are incorrect or incomplete,
|
||||
we want to hear from you!
|
||||
|
||||
Our main forum for discussion is the project's [GitHub issue
|
||||
tracker](https://github.com/python/typeshed/issues). This is the right
|
||||
place to start a discussion of any of the above or most any other
|
||||
topic concerning the project.
|
||||
|
||||
If you have general questions about typing with Python, or you need
|
||||
a review of your type annotations or stubs outside of typeshed, head over to
|
||||
[our discussion forum](https://github.com/python/typing/discussions).
|
||||
For less formal discussion, try the typing chat room on
|
||||
[gitter.im](https://gitter.im/python/typing). Some typeshed maintainers
|
||||
are almost always present; feel free to find us there and we're happy
|
||||
to chat. Substantive technical discussion will be directed to the
|
||||
issue tracker.
|
||||
@@ -1 +0,0 @@
|
||||
dcab6e88883c629ede9637fb011958f8b4918f52
|
||||
@@ -1,311 +0,0 @@
|
||||
# The structure of this file is as follows:
|
||||
# - Blank lines and comments starting with `#` are ignored.
|
||||
# - Lines contain the name of a module, followed by a colon,
|
||||
# a space, and a version range (for example: `symbol: 3.0-3.9`).
|
||||
#
|
||||
# Version ranges may be of the form "X.Y-A.B" or "X.Y-". The
|
||||
# first form means that a module was introduced in version X.Y and last
|
||||
# available in version A.B. The second form means that the module was
|
||||
# introduced in version X.Y and is still available in the latest
|
||||
# version of Python.
|
||||
#
|
||||
# If a submodule is not listed separately, it has the same lifetime as
|
||||
# its parent module.
|
||||
#
|
||||
# Python versions before 3.0 are ignored, so any module that was already
|
||||
# present in 3.0 will have "3.0" as its minimum version. Version ranges
|
||||
# for unsupported versions of Python 3 are generally accurate but we do
|
||||
# not guarantee their correctness.
|
||||
|
||||
__future__: 3.0-
|
||||
__main__: 3.0-
|
||||
_ast: 3.0-
|
||||
_bisect: 3.0-
|
||||
_bootlocale: 3.4-3.9
|
||||
_codecs: 3.0-
|
||||
_collections_abc: 3.3-
|
||||
_compat_pickle: 3.1-
|
||||
_compression: 3.5-
|
||||
_csv: 3.0-
|
||||
_ctypes: 3.0-
|
||||
_curses: 3.0-
|
||||
_decimal: 3.3-
|
||||
_dummy_thread: 3.0-3.8
|
||||
_dummy_threading: 3.0-3.8
|
||||
_heapq: 3.0-
|
||||
_imp: 3.0-
|
||||
_interpchannels: 3.13-
|
||||
_json: 3.0-
|
||||
_locale: 3.0-
|
||||
_lsprof: 3.0-
|
||||
_markupbase: 3.0-
|
||||
_msi: 3.0-
|
||||
_operator: 3.4-
|
||||
_osx_support: 3.0-
|
||||
_posixsubprocess: 3.2-
|
||||
_py_abc: 3.7-
|
||||
_pydecimal: 3.5-
|
||||
_random: 3.0-
|
||||
_sitebuiltins: 3.4-
|
||||
_socket: 3.0- # present in 3.0 at runtime, but not in typeshed
|
||||
_stat: 3.4-
|
||||
_thread: 3.0-
|
||||
_threading_local: 3.0-
|
||||
_tkinter: 3.0-
|
||||
_tracemalloc: 3.4-
|
||||
_typeshed: 3.0- # not present at runtime, only for type checking
|
||||
_warnings: 3.0-
|
||||
_weakref: 3.0-
|
||||
_weakrefset: 3.0-
|
||||
_winapi: 3.3-
|
||||
abc: 3.0-
|
||||
aifc: 3.0-3.12
|
||||
antigravity: 3.0-
|
||||
argparse: 3.0-
|
||||
array: 3.0-
|
||||
ast: 3.0-
|
||||
asynchat: 3.0-3.11
|
||||
asyncio: 3.4-
|
||||
asyncio.exceptions: 3.8-
|
||||
asyncio.format_helpers: 3.7-
|
||||
asyncio.mixins: 3.10-
|
||||
asyncio.runners: 3.7-
|
||||
asyncio.staggered: 3.8-
|
||||
asyncio.taskgroups: 3.11-
|
||||
asyncio.threads: 3.9-
|
||||
asyncio.timeouts: 3.11-
|
||||
asyncio.trsock: 3.8-
|
||||
asyncore: 3.0-3.11
|
||||
atexit: 3.0-
|
||||
audioop: 3.0-3.12
|
||||
base64: 3.0-
|
||||
bdb: 3.0-
|
||||
binascii: 3.0-
|
||||
binhex: 3.0-3.10
|
||||
bisect: 3.0-
|
||||
builtins: 3.0-
|
||||
bz2: 3.0-
|
||||
cProfile: 3.0-
|
||||
calendar: 3.0-
|
||||
cgi: 3.0-3.12
|
||||
cgitb: 3.0-3.12
|
||||
chunk: 3.0-3.12
|
||||
cmath: 3.0-
|
||||
cmd: 3.0-
|
||||
code: 3.0-
|
||||
codecs: 3.0-
|
||||
codeop: 3.0-
|
||||
collections: 3.0-
|
||||
collections.abc: 3.3-
|
||||
colorsys: 3.0-
|
||||
compileall: 3.0-
|
||||
concurrent: 3.2-
|
||||
configparser: 3.0-
|
||||
contextlib: 3.0-
|
||||
contextvars: 3.7-
|
||||
copy: 3.0-
|
||||
copyreg: 3.0-
|
||||
crypt: 3.0-3.12
|
||||
csv: 3.0-
|
||||
ctypes: 3.0-
|
||||
curses: 3.0-
|
||||
dataclasses: 3.7-
|
||||
datetime: 3.0-
|
||||
dbm: 3.0-
|
||||
decimal: 3.0-
|
||||
difflib: 3.0-
|
||||
dis: 3.0-
|
||||
distutils: 3.0-3.11
|
||||
distutils.command.bdist_msi: 3.0-3.10
|
||||
distutils.command.bdist_wininst: 3.0-3.9
|
||||
doctest: 3.0-
|
||||
dummy_threading: 3.0-3.8
|
||||
email: 3.0-
|
||||
encodings: 3.0-
|
||||
ensurepip: 3.0-
|
||||
enum: 3.4-
|
||||
errno: 3.0-
|
||||
faulthandler: 3.3-
|
||||
fcntl: 3.0-
|
||||
filecmp: 3.0-
|
||||
fileinput: 3.0-
|
||||
fnmatch: 3.0-
|
||||
formatter: 3.0-3.9
|
||||
fractions: 3.0-
|
||||
ftplib: 3.0-
|
||||
functools: 3.0-
|
||||
gc: 3.0-
|
||||
genericpath: 3.0-
|
||||
getopt: 3.0-
|
||||
getpass: 3.0-
|
||||
gettext: 3.0-
|
||||
glob: 3.0-
|
||||
graphlib: 3.9-
|
||||
grp: 3.0-
|
||||
gzip: 3.0-
|
||||
hashlib: 3.0-
|
||||
heapq: 3.0-
|
||||
hmac: 3.0-
|
||||
html: 3.0-
|
||||
http: 3.0-
|
||||
imaplib: 3.0-
|
||||
imghdr: 3.0-3.12
|
||||
imp: 3.0-3.11
|
||||
importlib: 3.0-
|
||||
importlib._abc: 3.10-
|
||||
importlib.metadata: 3.8-
|
||||
importlib.metadata._meta: 3.10-
|
||||
importlib.readers: 3.10-
|
||||
importlib.resources: 3.7-
|
||||
importlib.resources.abc: 3.11-
|
||||
importlib.resources.readers: 3.11-
|
||||
importlib.resources.simple: 3.11-
|
||||
importlib.simple: 3.11-
|
||||
inspect: 3.0-
|
||||
io: 3.0-
|
||||
ipaddress: 3.3-
|
||||
itertools: 3.0-
|
||||
json: 3.0-
|
||||
keyword: 3.0-
|
||||
lib2to3: 3.0-3.12
|
||||
linecache: 3.0-
|
||||
locale: 3.0-
|
||||
logging: 3.0-
|
||||
lzma: 3.3-
|
||||
mailbox: 3.0-
|
||||
mailcap: 3.0-3.12
|
||||
marshal: 3.0-
|
||||
math: 3.0-
|
||||
mimetypes: 3.0-
|
||||
mmap: 3.0-
|
||||
modulefinder: 3.0-
|
||||
msilib: 3.0-3.12
|
||||
msvcrt: 3.0-
|
||||
multiprocessing: 3.0-
|
||||
multiprocessing.resource_tracker: 3.8-
|
||||
multiprocessing.shared_memory: 3.8-
|
||||
netrc: 3.0-
|
||||
nis: 3.0-3.12
|
||||
nntplib: 3.0-3.12
|
||||
nt: 3.0-
|
||||
ntpath: 3.0-
|
||||
nturl2path: 3.0-
|
||||
numbers: 3.0-
|
||||
opcode: 3.0-
|
||||
operator: 3.0-
|
||||
optparse: 3.0-
|
||||
os: 3.0-
|
||||
ossaudiodev: 3.0-3.12
|
||||
parser: 3.0-3.9
|
||||
pathlib: 3.4-
|
||||
pdb: 3.0-
|
||||
pickle: 3.0-
|
||||
pickletools: 3.0-
|
||||
pipes: 3.0-3.12
|
||||
pkgutil: 3.0-
|
||||
platform: 3.0-
|
||||
plistlib: 3.0-
|
||||
poplib: 3.0-
|
||||
posix: 3.0-
|
||||
posixpath: 3.0-
|
||||
pprint: 3.0-
|
||||
profile: 3.0-
|
||||
pstats: 3.0-
|
||||
pty: 3.0-
|
||||
pwd: 3.0-
|
||||
py_compile: 3.0-
|
||||
pyclbr: 3.0-
|
||||
pydoc: 3.0-
|
||||
pydoc_data: 3.0-
|
||||
pyexpat: 3.0-
|
||||
queue: 3.0-
|
||||
quopri: 3.0-
|
||||
random: 3.0-
|
||||
re: 3.0-
|
||||
readline: 3.0-
|
||||
reprlib: 3.0-
|
||||
resource: 3.0-
|
||||
rlcompleter: 3.0-
|
||||
runpy: 3.0-
|
||||
sched: 3.0-
|
||||
secrets: 3.6-
|
||||
select: 3.0-
|
||||
selectors: 3.4-
|
||||
shelve: 3.0-
|
||||
shlex: 3.0-
|
||||
shutil: 3.0-
|
||||
signal: 3.0-
|
||||
site: 3.0-
|
||||
smtpd: 3.0-3.11
|
||||
smtplib: 3.0-
|
||||
sndhdr: 3.0-3.12
|
||||
socket: 3.0-
|
||||
socketserver: 3.0-
|
||||
spwd: 3.0-3.12
|
||||
sqlite3: 3.0-
|
||||
sre_compile: 3.0-
|
||||
sre_constants: 3.0-
|
||||
sre_parse: 3.0-
|
||||
ssl: 3.0-
|
||||
stat: 3.0-
|
||||
statistics: 3.4-
|
||||
string: 3.0-
|
||||
stringprep: 3.0-
|
||||
struct: 3.0-
|
||||
subprocess: 3.0-
|
||||
sunau: 3.0-3.12
|
||||
symbol: 3.0-3.9
|
||||
symtable: 3.0-
|
||||
sys: 3.0-
|
||||
sys._monitoring: 3.12- # Doesn't actually exist. See comments in the stub.
|
||||
sysconfig: 3.0-
|
||||
syslog: 3.0-
|
||||
tabnanny: 3.0-
|
||||
tarfile: 3.0-
|
||||
telnetlib: 3.0-3.12
|
||||
tempfile: 3.0-
|
||||
termios: 3.0-
|
||||
textwrap: 3.0-
|
||||
this: 3.0-
|
||||
threading: 3.0-
|
||||
time: 3.0-
|
||||
timeit: 3.0-
|
||||
tkinter: 3.0-
|
||||
tkinter.tix: 3.0-3.12
|
||||
token: 3.0-
|
||||
tokenize: 3.0-
|
||||
tomllib: 3.11-
|
||||
trace: 3.0-
|
||||
traceback: 3.0-
|
||||
tracemalloc: 3.4-
|
||||
tty: 3.0-
|
||||
turtle: 3.0-
|
||||
types: 3.0-
|
||||
typing: 3.5-
|
||||
typing_extensions: 3.0-
|
||||
unicodedata: 3.0-
|
||||
unittest: 3.0-
|
||||
unittest._log: 3.9-
|
||||
unittest.async_case: 3.8-
|
||||
urllib: 3.0-
|
||||
uu: 3.0-3.12
|
||||
uuid: 3.0-
|
||||
venv: 3.3-
|
||||
warnings: 3.0-
|
||||
wave: 3.0-
|
||||
weakref: 3.0-
|
||||
webbrowser: 3.0-
|
||||
winreg: 3.0-
|
||||
winsound: 3.0-
|
||||
wsgiref: 3.0-
|
||||
wsgiref.types: 3.11-
|
||||
xdrlib: 3.0-3.12
|
||||
xml: 3.0-
|
||||
xmlrpc: 3.0-
|
||||
xxlimited: 3.2-
|
||||
zipapp: 3.5-
|
||||
zipfile: 3.0-
|
||||
zipfile._path: 3.12-
|
||||
zipimport: 3.0-
|
||||
zlib: 3.0-
|
||||
zoneinfo: 3.9-
|
||||
@@ -1,36 +0,0 @@
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
_VersionInfo: TypeAlias = tuple[int, int, int, str, int]
|
||||
|
||||
class _Feature:
|
||||
def __init__(self, optionalRelease: _VersionInfo, mandatoryRelease: _VersionInfo | None, compiler_flag: int) -> None: ...
|
||||
def getOptionalRelease(self) -> _VersionInfo: ...
|
||||
def getMandatoryRelease(self) -> _VersionInfo | None: ...
|
||||
compiler_flag: int
|
||||
|
||||
absolute_import: _Feature
|
||||
division: _Feature
|
||||
generators: _Feature
|
||||
nested_scopes: _Feature
|
||||
print_function: _Feature
|
||||
unicode_literals: _Feature
|
||||
with_statement: _Feature
|
||||
barry_as_FLUFL: _Feature
|
||||
generator_stop: _Feature
|
||||
annotations: _Feature
|
||||
|
||||
all_feature_names: list[str] # undocumented
|
||||
|
||||
__all__ = [
|
||||
"all_feature_names",
|
||||
"absolute_import",
|
||||
"division",
|
||||
"generators",
|
||||
"nested_scopes",
|
||||
"print_function",
|
||||
"unicode_literals",
|
||||
"with_statement",
|
||||
"barry_as_FLUFL",
|
||||
"generator_stop",
|
||||
"annotations",
|
||||
]
|
||||
@@ -1,3 +0,0 @@
|
||||
from typing import Any
|
||||
|
||||
def __getattr__(name: str) -> Any: ...
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,84 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import SupportsLenAndGetItem, SupportsRichComparisonT
|
||||
from collections.abc import Callable, MutableSequence
|
||||
from typing import TypeVar, overload
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
@overload
|
||||
def bisect_left(
|
||||
a: SupportsLenAndGetItem[SupportsRichComparisonT],
|
||||
x: SupportsRichComparisonT,
|
||||
lo: int = 0,
|
||||
hi: int | None = None,
|
||||
*,
|
||||
key: None = None,
|
||||
) -> int: ...
|
||||
@overload
|
||||
def bisect_left(
|
||||
a: SupportsLenAndGetItem[_T],
|
||||
x: SupportsRichComparisonT,
|
||||
lo: int = 0,
|
||||
hi: int | None = None,
|
||||
*,
|
||||
key: Callable[[_T], SupportsRichComparisonT],
|
||||
) -> int: ...
|
||||
@overload
|
||||
def bisect_right(
|
||||
a: SupportsLenAndGetItem[SupportsRichComparisonT],
|
||||
x: SupportsRichComparisonT,
|
||||
lo: int = 0,
|
||||
hi: int | None = None,
|
||||
*,
|
||||
key: None = None,
|
||||
) -> int: ...
|
||||
@overload
|
||||
def bisect_right(
|
||||
a: SupportsLenAndGetItem[_T],
|
||||
x: SupportsRichComparisonT,
|
||||
lo: int = 0,
|
||||
hi: int | None = None,
|
||||
*,
|
||||
key: Callable[[_T], SupportsRichComparisonT],
|
||||
) -> int: ...
|
||||
@overload
|
||||
def insort_left(
|
||||
a: MutableSequence[SupportsRichComparisonT],
|
||||
x: SupportsRichComparisonT,
|
||||
lo: int = 0,
|
||||
hi: int | None = None,
|
||||
*,
|
||||
key: None = None,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def insort_left(
|
||||
a: MutableSequence[_T], x: _T, lo: int = 0, hi: int | None = None, *, key: Callable[[_T], SupportsRichComparisonT]
|
||||
) -> None: ...
|
||||
@overload
|
||||
def insort_right(
|
||||
a: MutableSequence[SupportsRichComparisonT],
|
||||
x: SupportsRichComparisonT,
|
||||
lo: int = 0,
|
||||
hi: int | None = None,
|
||||
*,
|
||||
key: None = None,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def insort_right(
|
||||
a: MutableSequence[_T], x: _T, lo: int = 0, hi: int | None = None, *, key: Callable[[_T], SupportsRichComparisonT]
|
||||
) -> None: ...
|
||||
|
||||
else:
|
||||
def bisect_left(
|
||||
a: SupportsLenAndGetItem[SupportsRichComparisonT], x: SupportsRichComparisonT, lo: int = 0, hi: int | None = None
|
||||
) -> int: ...
|
||||
def bisect_right(
|
||||
a: SupportsLenAndGetItem[SupportsRichComparisonT], x: SupportsRichComparisonT, lo: int = 0, hi: int | None = None
|
||||
) -> int: ...
|
||||
def insort_left(
|
||||
a: MutableSequence[SupportsRichComparisonT], x: SupportsRichComparisonT, lo: int = 0, hi: int | None = None
|
||||
) -> None: ...
|
||||
def insort_right(
|
||||
a: MutableSequence[SupportsRichComparisonT], x: SupportsRichComparisonT, lo: int = 0, hi: int | None = None
|
||||
) -> None: ...
|
||||
@@ -1 +0,0 @@
|
||||
def getpreferredencoding(do_setlocale: bool = True) -> str: ...
|
||||
@@ -1,133 +0,0 @@
|
||||
import codecs
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer
|
||||
from collections.abc import Callable
|
||||
from typing import Literal, overload
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# This type is not exposed; it is defined in unicodeobject.c
|
||||
class _EncodingMap:
|
||||
def size(self) -> int: ...
|
||||
|
||||
_CharMap: TypeAlias = dict[int, int] | _EncodingMap
|
||||
_Handler: TypeAlias = Callable[[UnicodeError], tuple[str | bytes, int]]
|
||||
_SearchFunction: TypeAlias = Callable[[str], codecs.CodecInfo | None]
|
||||
|
||||
def register(search_function: _SearchFunction, /) -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
def unregister(search_function: _SearchFunction, /) -> None: ...
|
||||
|
||||
def register_error(errors: str, handler: _Handler, /) -> None: ...
|
||||
def lookup_error(name: str, /) -> _Handler: ...
|
||||
|
||||
# The type ignore on `encode` and `decode` is to avoid issues with overlapping overloads, for more details, see #300
|
||||
# https://docs.python.org/3/library/codecs.html#binary-transforms
|
||||
_BytesToBytesEncoding: TypeAlias = Literal[
|
||||
"base64",
|
||||
"base_64",
|
||||
"base64_codec",
|
||||
"bz2",
|
||||
"bz2_codec",
|
||||
"hex",
|
||||
"hex_codec",
|
||||
"quopri",
|
||||
"quotedprintable",
|
||||
"quoted_printable",
|
||||
"quopri_codec",
|
||||
"uu",
|
||||
"uu_codec",
|
||||
"zip",
|
||||
"zlib",
|
||||
"zlib_codec",
|
||||
]
|
||||
# https://docs.python.org/3/library/codecs.html#text-transforms
|
||||
_StrToStrEncoding: TypeAlias = Literal["rot13", "rot_13"]
|
||||
|
||||
@overload
|
||||
def encode(obj: ReadableBuffer, encoding: _BytesToBytesEncoding, errors: str = "strict") -> bytes: ...
|
||||
@overload
|
||||
def encode(obj: str, encoding: _StrToStrEncoding, errors: str = "strict") -> str: ... # type: ignore[overload-overlap]
|
||||
@overload
|
||||
def encode(obj: str, encoding: str = "utf-8", errors: str = "strict") -> bytes: ...
|
||||
@overload
|
||||
def decode(obj: ReadableBuffer, encoding: _BytesToBytesEncoding, errors: str = "strict") -> bytes: ... # type: ignore[overload-overlap]
|
||||
@overload
|
||||
def decode(obj: str, encoding: _StrToStrEncoding, errors: str = "strict") -> str: ...
|
||||
|
||||
# these are documented as text encodings but in practice they also accept str as input
|
||||
@overload
|
||||
def decode(
|
||||
obj: str,
|
||||
encoding: Literal["unicode_escape", "unicode-escape", "raw_unicode_escape", "raw-unicode-escape"],
|
||||
errors: str = "strict",
|
||||
) -> str: ...
|
||||
|
||||
# hex is officially documented as a bytes to bytes encoding, but it appears to also work with str
|
||||
@overload
|
||||
def decode(obj: str, encoding: Literal["hex", "hex_codec"], errors: str = "strict") -> bytes: ...
|
||||
@overload
|
||||
def decode(obj: ReadableBuffer, encoding: str = "utf-8", errors: str = "strict") -> str: ...
|
||||
def lookup(encoding: str, /) -> codecs.CodecInfo: ...
|
||||
def charmap_build(map: str, /) -> _CharMap: ...
|
||||
def ascii_decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
|
||||
def ascii_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def charmap_decode(data: ReadableBuffer, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[str, int]: ...
|
||||
def charmap_encode(str: str, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[bytes, int]: ...
|
||||
def escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
|
||||
def escape_encode(data: bytes, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def latin_1_decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
|
||||
def latin_1_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
def raw_unicode_escape_decode(
|
||||
data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /
|
||||
) -> tuple[str, int]: ...
|
||||
|
||||
else:
|
||||
def raw_unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
|
||||
|
||||
def raw_unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def readbuffer_encode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
def unicode_escape_decode(
|
||||
data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /
|
||||
) -> tuple[str, int]: ...
|
||||
|
||||
else:
|
||||
def unicode_escape_decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
|
||||
|
||||
def unicode_escape_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def utf_16_be_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_16_be_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def utf_16_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_16_encode(str: str, errors: str | None = None, byteorder: int = 0, /) -> tuple[bytes, int]: ...
|
||||
def utf_16_ex_decode(
|
||||
data: ReadableBuffer, errors: str | None = None, byteorder: int = 0, final: bool = False, /
|
||||
) -> tuple[str, int, int]: ...
|
||||
def utf_16_le_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_16_le_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def utf_32_be_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_32_be_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def utf_32_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_32_encode(str: str, errors: str | None = None, byteorder: int = 0, /) -> tuple[bytes, int]: ...
|
||||
def utf_32_ex_decode(
|
||||
data: ReadableBuffer, errors: str | None = None, byteorder: int = 0, final: bool = False, /
|
||||
) -> tuple[str, int, int]: ...
|
||||
def utf_32_le_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_32_le_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def utf_7_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_7_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def utf_8_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def utf_8_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
def mbcs_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def mbcs_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def code_page_decode(
|
||||
codepage: int, data: ReadableBuffer, errors: str | None = None, final: bool = False, /
|
||||
) -> tuple[str, int]: ...
|
||||
def code_page_encode(code_page: int, str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
def oem_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ...
|
||||
def oem_encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
@@ -1,94 +0,0 @@
|
||||
import sys
|
||||
from abc import abstractmethod
|
||||
from types import MappingProxyType
|
||||
from typing import ( # noqa: Y022,Y038,Y057
|
||||
AbstractSet as Set,
|
||||
AsyncGenerator as AsyncGenerator,
|
||||
AsyncIterable as AsyncIterable,
|
||||
AsyncIterator as AsyncIterator,
|
||||
Awaitable as Awaitable,
|
||||
ByteString as ByteString,
|
||||
Callable as Callable,
|
||||
Collection as Collection,
|
||||
Container as Container,
|
||||
Coroutine as Coroutine,
|
||||
Generator as Generator,
|
||||
Generic,
|
||||
Hashable as Hashable,
|
||||
ItemsView as ItemsView,
|
||||
Iterable as Iterable,
|
||||
Iterator as Iterator,
|
||||
KeysView as KeysView,
|
||||
Mapping as Mapping,
|
||||
MappingView as MappingView,
|
||||
MutableMapping as MutableMapping,
|
||||
MutableSequence as MutableSequence,
|
||||
MutableSet as MutableSet,
|
||||
Protocol,
|
||||
Reversible as Reversible,
|
||||
Sequence as Sequence,
|
||||
Sized as Sized,
|
||||
TypeVar,
|
||||
ValuesView as ValuesView,
|
||||
final,
|
||||
runtime_checkable,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Awaitable",
|
||||
"Coroutine",
|
||||
"AsyncIterable",
|
||||
"AsyncIterator",
|
||||
"AsyncGenerator",
|
||||
"Hashable",
|
||||
"Iterable",
|
||||
"Iterator",
|
||||
"Generator",
|
||||
"Reversible",
|
||||
"Sized",
|
||||
"Container",
|
||||
"Callable",
|
||||
"Collection",
|
||||
"Set",
|
||||
"MutableSet",
|
||||
"Mapping",
|
||||
"MutableMapping",
|
||||
"MappingView",
|
||||
"KeysView",
|
||||
"ItemsView",
|
||||
"ValuesView",
|
||||
"Sequence",
|
||||
"MutableSequence",
|
||||
"ByteString",
|
||||
]
|
||||
if sys.version_info >= (3, 12):
|
||||
__all__ += ["Buffer"]
|
||||
|
||||
_KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers.
|
||||
_VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers.
|
||||
|
||||
@final
|
||||
class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@property
|
||||
def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ...
|
||||
|
||||
@final
|
||||
class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
if sys.version_info >= (3, 10):
|
||||
@property
|
||||
def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ...
|
||||
|
||||
@final
|
||||
class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@property
|
||||
def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ...
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
@runtime_checkable
|
||||
class Buffer(Protocol):
|
||||
@abstractmethod
|
||||
def __buffer__(self, flags: int, /) -> memoryview: ...
|
||||
@@ -1,8 +0,0 @@
|
||||
IMPORT_MAPPING: dict[str, str]
|
||||
NAME_MAPPING: dict[tuple[str, str], tuple[str, str]]
|
||||
PYTHON2_EXCEPTIONS: tuple[str, ...]
|
||||
MULTIPROCESSING_EXCEPTIONS: tuple[str, ...]
|
||||
REVERSE_IMPORT_MAPPING: dict[str, str]
|
||||
REVERSE_NAME_MAPPING: dict[tuple[str, str], tuple[str, str]]
|
||||
PYTHON3_OSERROR_EXCEPTIONS: tuple[str, ...]
|
||||
PYTHON3_IMPORTERROR_EXCEPTIONS: tuple[str, ...]
|
||||
@@ -1,25 +0,0 @@
|
||||
from _typeshed import WriteableBuffer
|
||||
from collections.abc import Callable
|
||||
from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase
|
||||
from typing import Any, Protocol
|
||||
|
||||
BUFFER_SIZE = DEFAULT_BUFFER_SIZE
|
||||
|
||||
class _Reader(Protocol):
|
||||
def read(self, n: int, /) -> bytes: ...
|
||||
def seekable(self) -> bool: ...
|
||||
def seek(self, n: int, /) -> Any: ...
|
||||
|
||||
class BaseStream(BufferedIOBase): ...
|
||||
|
||||
class DecompressReader(RawIOBase):
|
||||
def __init__(
|
||||
self,
|
||||
fp: _Reader,
|
||||
decomp_factory: Callable[..., object],
|
||||
trailing_error: type[Exception] | tuple[type[Exception], ...] = (),
|
||||
**decomp_args: Any,
|
||||
) -> None: ...
|
||||
def readinto(self, b: WriteableBuffer) -> int: ...
|
||||
def read(self, size: int = -1) -> bytes: ...
|
||||
def seek(self, offset: int, whence: int = 0) -> int: ...
|
||||
@@ -1,90 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import SupportsWrite
|
||||
from collections.abc import Iterable, Iterator
|
||||
from typing import Any, Final, Literal
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
__version__: Final[str]
|
||||
|
||||
QUOTE_ALL: Literal[1]
|
||||
QUOTE_MINIMAL: Literal[0]
|
||||
QUOTE_NONE: Literal[3]
|
||||
QUOTE_NONNUMERIC: Literal[2]
|
||||
if sys.version_info >= (3, 12):
|
||||
QUOTE_STRINGS: Literal[4]
|
||||
QUOTE_NOTNULL: Literal[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)
|
||||
_QuotingType: TypeAlias = int
|
||||
|
||||
class Error(Exception): ...
|
||||
|
||||
class Dialect:
|
||||
delimiter: str
|
||||
quotechar: str | None
|
||||
escapechar: str | None
|
||||
doublequote: bool
|
||||
skipinitialspace: bool
|
||||
lineterminator: str
|
||||
quoting: _QuotingType
|
||||
strict: bool
|
||||
def __init__(self) -> None: ...
|
||||
|
||||
_DialectLike: TypeAlias = str | Dialect | type[Dialect]
|
||||
|
||||
class _reader(Iterator[list[str]]):
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
line_num: int
|
||||
def __next__(self) -> list[str]: ...
|
||||
|
||||
class _writer:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
def writerow(self, row: Iterable[Any]) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
|
||||
|
||||
def writer(
|
||||
csvfile: SupportsWrite[str],
|
||||
dialect: _DialectLike = "excel",
|
||||
*,
|
||||
delimiter: str = ",",
|
||||
quotechar: str | None = '"',
|
||||
escapechar: str | None = None,
|
||||
doublequote: bool = True,
|
||||
skipinitialspace: bool = False,
|
||||
lineterminator: str = "\r\n",
|
||||
quoting: _QuotingType = 0,
|
||||
strict: bool = False,
|
||||
) -> _writer: ...
|
||||
def reader(
|
||||
csvfile: Iterable[str],
|
||||
dialect: _DialectLike = "excel",
|
||||
*,
|
||||
delimiter: str = ",",
|
||||
quotechar: str | None = '"',
|
||||
escapechar: str | None = None,
|
||||
doublequote: bool = True,
|
||||
skipinitialspace: bool = False,
|
||||
lineterminator: str = "\r\n",
|
||||
quoting: _QuotingType = 0,
|
||||
strict: bool = False,
|
||||
) -> _reader: ...
|
||||
def register_dialect(
|
||||
name: str,
|
||||
dialect: type[Dialect] = ...,
|
||||
*,
|
||||
delimiter: str = ",",
|
||||
quotechar: str | None = '"',
|
||||
escapechar: str | None = None,
|
||||
doublequote: bool = True,
|
||||
skipinitialspace: bool = False,
|
||||
lineterminator: str = "\r\n",
|
||||
quoting: _QuotingType = 0,
|
||||
strict: bool = False,
|
||||
) -> None: ...
|
||||
def unregister_dialect(name: str) -> None: ...
|
||||
def get_dialect(name: str) -> Dialect: ...
|
||||
def list_dialects() -> list[str]: ...
|
||||
def field_size_limit(new_limit: int = ...) -> int: ...
|
||||
@@ -1,211 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer, WriteableBuffer
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
|
||||
from ctypes import CDLL, ArgumentError as ArgumentError
|
||||
from typing import Any, ClassVar, Generic, TypeVar, overload
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from types import GenericAlias
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_CT = TypeVar("_CT", bound=_CData)
|
||||
|
||||
FUNCFLAG_CDECL: int
|
||||
FUNCFLAG_PYTHONAPI: int
|
||||
FUNCFLAG_USE_ERRNO: int
|
||||
FUNCFLAG_USE_LASTERROR: int
|
||||
RTLD_GLOBAL: int
|
||||
RTLD_LOCAL: int
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
CTYPES_MAX_ARGCOUNT: int
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
SIZEOF_TIME_T: int
|
||||
|
||||
if sys.platform == "win32":
|
||||
# Description, Source, HelpFile, HelpContext, scode
|
||||
_COMError_Details: TypeAlias = tuple[str | None, str | None, str | None, int | None, int | None]
|
||||
|
||||
class COMError(Exception):
|
||||
hresult: int
|
||||
text: str | None
|
||||
details: _COMError_Details
|
||||
|
||||
def __init__(self, hresult: int, text: str | None, details: _COMError_Details) -> None: ...
|
||||
|
||||
def CopyComPointer(src: _PointerLike, dst: _PointerLike | _CArgObject) -> int: ...
|
||||
|
||||
FUNCFLAG_HRESULT: int
|
||||
FUNCFLAG_STDCALL: int
|
||||
|
||||
def FormatError(code: int = ...) -> str: ...
|
||||
def get_last_error() -> int: ...
|
||||
def set_last_error(value: int) -> int: ...
|
||||
def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ...
|
||||
def FreeLibrary(handle: int, /) -> None: ...
|
||||
|
||||
class _CDataMeta(type):
|
||||
# By default mypy complains about the following two methods, because strictly speaking cls
|
||||
# might not be a Type[_CT]. However this can never actually happen, because the only class that
|
||||
# uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here.
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc]
|
||||
|
||||
class _CData(metaclass=_CDataMeta):
|
||||
_b_base_: int
|
||||
_b_needsfree_: bool
|
||||
_objects: Mapping[Any, int] | None
|
||||
# At runtime the following classmethods are available only on classes, not
|
||||
# on instances. This can't be reflected properly in the type system:
|
||||
#
|
||||
# Structure.from_buffer(...) # valid at runtime
|
||||
# Structure(...).from_buffer(...) # invalid at runtime
|
||||
#
|
||||
|
||||
@classmethod
|
||||
def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_address(cls, address: int) -> Self: ...
|
||||
@classmethod
|
||||
def from_param(cls, obj: Any) -> Self | _CArgObject: ...
|
||||
@classmethod
|
||||
def in_dll(cls, library: CDLL, name: str) -> Self: ...
|
||||
def __buffer__(self, flags: int, /) -> memoryview: ...
|
||||
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
|
||||
|
||||
class _SimpleCData(_CData, Generic[_T]):
|
||||
value: _T
|
||||
# The TypeVar can be unsolved here,
|
||||
# but we can't use overloads without creating many, many mypy false-positive errors
|
||||
def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse]
|
||||
|
||||
class _CanCastTo(_CData): ...
|
||||
class _PointerLike(_CanCastTo): ...
|
||||
|
||||
class _Pointer(_PointerLike, _CData, Generic[_CT]):
|
||||
_type_: type[_CT]
|
||||
contents: _CT
|
||||
@overload
|
||||
def __init__(self) -> None: ...
|
||||
@overload
|
||||
def __init__(self, arg: _CT) -> None: ...
|
||||
@overload
|
||||
def __getitem__(self, key: int, /) -> Any: ...
|
||||
@overload
|
||||
def __getitem__(self, key: slice, /) -> list[Any]: ...
|
||||
def __setitem__(self, key: int, value: Any, /) -> None: ...
|
||||
|
||||
def POINTER(type: type[_CT]) -> type[_Pointer[_CT]]: ...
|
||||
def pointer(arg: _CT, /) -> _Pointer[_CT]: ...
|
||||
|
||||
class _CArgObject: ...
|
||||
|
||||
def byref(obj: _CData, offset: int = ...) -> _CArgObject: ...
|
||||
|
||||
_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData]
|
||||
_PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any]
|
||||
|
||||
class CFuncPtr(_PointerLike, _CData):
|
||||
restype: type[_CData] | Callable[[int], Any] | None
|
||||
argtypes: Sequence[type[_CData]]
|
||||
errcheck: _ECT
|
||||
# Abstract attribute that must be defined on subclasses
|
||||
_flags_: ClassVar[int]
|
||||
@overload
|
||||
def __init__(self) -> None: ...
|
||||
@overload
|
||||
def __init__(self, address: int, /) -> None: ...
|
||||
@overload
|
||||
def __init__(self, callable: Callable[..., Any], /) -> None: ...
|
||||
@overload
|
||||
def __init__(self, func_spec: tuple[str | int, CDLL], paramflags: tuple[_PF, ...] | None = ..., /) -> None: ...
|
||||
if sys.platform == "win32":
|
||||
@overload
|
||||
def __init__(
|
||||
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., /
|
||||
) -> None: ...
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
|
||||
_GetT = TypeVar("_GetT")
|
||||
_SetT = TypeVar("_SetT")
|
||||
|
||||
class _CField(Generic[_CT, _GetT, _SetT]):
|
||||
offset: int
|
||||
size: int
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
|
||||
def __set__(self, instance: Any, value: _SetT, /) -> None: ...
|
||||
|
||||
class _StructUnionMeta(_CDataMeta):
|
||||
_fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]]
|
||||
_pack_: int
|
||||
_anonymous_: Sequence[str]
|
||||
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
|
||||
|
||||
class _StructUnionBase(_CData, metaclass=_StructUnionMeta):
|
||||
def __init__(self, *args: Any, **kw: Any) -> None: ...
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
|
||||
class Union(_StructUnionBase): ...
|
||||
class Structure(_StructUnionBase): ...
|
||||
|
||||
class Array(_CData, Generic[_CT]):
|
||||
@property
|
||||
@abstractmethod
|
||||
def _length_(self) -> int: ...
|
||||
@_length_.setter
|
||||
def _length_(self, value: int) -> None: ...
|
||||
@property
|
||||
@abstractmethod
|
||||
def _type_(self) -> type[_CT]: ...
|
||||
@_type_.setter
|
||||
def _type_(self, value: type[_CT]) -> None: ...
|
||||
# Note: only available if _CT == c_char
|
||||
@property
|
||||
def raw(self) -> bytes: ...
|
||||
@raw.setter
|
||||
def raw(self, value: ReadableBuffer) -> None: ...
|
||||
value: Any # Note: bytes if _CT == c_char, str if _CT == c_wchar, unavailable otherwise
|
||||
# TODO These methods cannot be annotated correctly at the moment.
|
||||
# All of these "Any"s stand for the array's element type, but it's not possible to use _CT
|
||||
# here, because of a special feature of ctypes.
|
||||
# By default, when accessing an element of an Array[_CT], the returned object has type _CT.
|
||||
# However, when _CT is a "simple type" like c_int, ctypes automatically "unboxes" the object
|
||||
# and converts it to the corresponding Python primitive. For example, when accessing an element
|
||||
# of an Array[c_int], a Python int object is returned, not a c_int.
|
||||
# This behavior does *not* apply to subclasses of "simple types".
|
||||
# If MyInt is a subclass of c_int, then accessing an element of an Array[MyInt] returns
|
||||
# a MyInt, not an int.
|
||||
# This special behavior is not easy to model in a stub, so for now all places where
|
||||
# the array element type would belong are annotated with Any instead.
|
||||
def __init__(self, *args: Any) -> None: ...
|
||||
@overload
|
||||
def __getitem__(self, key: int, /) -> Any: ...
|
||||
@overload
|
||||
def __getitem__(self, key: slice, /) -> list[Any]: ...
|
||||
@overload
|
||||
def __setitem__(self, key: int, value: Any, /) -> None: ...
|
||||
@overload
|
||||
def __setitem__(self, key: slice, value: Iterable[Any], /) -> None: ...
|
||||
def __iter__(self) -> Iterator[Any]: ...
|
||||
# Can't inherit from Sized because the metaclass conflict between
|
||||
# Sized and _CData prevents using _CDataMeta.
|
||||
def __len__(self) -> int: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
|
||||
|
||||
def addressof(obj: _CData) -> int: ...
|
||||
def alignment(obj_or_type: _CData | type[_CData]) -> int: ...
|
||||
def get_errno() -> int: ...
|
||||
def resize(obj: _CData, size: int) -> None: ...
|
||||
def set_errno(value: int) -> int: ...
|
||||
def sizeof(obj_or_type: _CData | type[_CData]) -> int: ...
|
||||
@@ -1,565 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import ReadOnlyBuffer, SupportsRead
|
||||
from typing import IO, Any, NamedTuple, final, overload
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# NOTE: This module is ordinarily only available on Unix, but the windows-curses
|
||||
# package makes it available on Windows as well with the same contents.
|
||||
|
||||
# Handled by PyCurses_ConvertToChtype in _cursesmodule.c.
|
||||
_ChType: TypeAlias = str | bytes | int
|
||||
|
||||
# ACS codes are only initialized after initscr is called
|
||||
ACS_BBSS: int
|
||||
ACS_BLOCK: int
|
||||
ACS_BOARD: int
|
||||
ACS_BSBS: int
|
||||
ACS_BSSB: int
|
||||
ACS_BSSS: int
|
||||
ACS_BTEE: int
|
||||
ACS_BULLET: int
|
||||
ACS_CKBOARD: int
|
||||
ACS_DARROW: int
|
||||
ACS_DEGREE: int
|
||||
ACS_DIAMOND: int
|
||||
ACS_GEQUAL: int
|
||||
ACS_HLINE: int
|
||||
ACS_LANTERN: int
|
||||
ACS_LARROW: int
|
||||
ACS_LEQUAL: int
|
||||
ACS_LLCORNER: int
|
||||
ACS_LRCORNER: int
|
||||
ACS_LTEE: int
|
||||
ACS_NEQUAL: int
|
||||
ACS_PI: int
|
||||
ACS_PLMINUS: int
|
||||
ACS_PLUS: int
|
||||
ACS_RARROW: int
|
||||
ACS_RTEE: int
|
||||
ACS_S1: int
|
||||
ACS_S3: int
|
||||
ACS_S7: int
|
||||
ACS_S9: int
|
||||
ACS_SBBS: int
|
||||
ACS_SBSB: int
|
||||
ACS_SBSS: int
|
||||
ACS_SSBB: int
|
||||
ACS_SSBS: int
|
||||
ACS_SSSB: int
|
||||
ACS_SSSS: int
|
||||
ACS_STERLING: int
|
||||
ACS_TTEE: int
|
||||
ACS_UARROW: int
|
||||
ACS_ULCORNER: int
|
||||
ACS_URCORNER: int
|
||||
ACS_VLINE: int
|
||||
ALL_MOUSE_EVENTS: int
|
||||
A_ALTCHARSET: int
|
||||
A_ATTRIBUTES: int
|
||||
A_BLINK: int
|
||||
A_BOLD: int
|
||||
A_CHARTEXT: int
|
||||
A_COLOR: int
|
||||
A_DIM: int
|
||||
A_HORIZONTAL: int
|
||||
A_INVIS: int
|
||||
A_ITALIC: int
|
||||
A_LEFT: int
|
||||
A_LOW: int
|
||||
A_NORMAL: int
|
||||
A_PROTECT: int
|
||||
A_REVERSE: int
|
||||
A_RIGHT: int
|
||||
A_STANDOUT: int
|
||||
A_TOP: int
|
||||
A_UNDERLINE: int
|
||||
A_VERTICAL: int
|
||||
BUTTON1_CLICKED: int
|
||||
BUTTON1_DOUBLE_CLICKED: int
|
||||
BUTTON1_PRESSED: int
|
||||
BUTTON1_RELEASED: int
|
||||
BUTTON1_TRIPLE_CLICKED: int
|
||||
BUTTON2_CLICKED: int
|
||||
BUTTON2_DOUBLE_CLICKED: int
|
||||
BUTTON2_PRESSED: int
|
||||
BUTTON2_RELEASED: int
|
||||
BUTTON2_TRIPLE_CLICKED: int
|
||||
BUTTON3_CLICKED: int
|
||||
BUTTON3_DOUBLE_CLICKED: int
|
||||
BUTTON3_PRESSED: int
|
||||
BUTTON3_RELEASED: int
|
||||
BUTTON3_TRIPLE_CLICKED: int
|
||||
BUTTON4_CLICKED: int
|
||||
BUTTON4_DOUBLE_CLICKED: int
|
||||
BUTTON4_PRESSED: int
|
||||
BUTTON4_RELEASED: int
|
||||
BUTTON4_TRIPLE_CLICKED: int
|
||||
# Darwin ncurses doesn't provide BUTTON5_* constants
|
||||
if sys.version_info >= (3, 10) and sys.platform != "darwin":
|
||||
BUTTON5_PRESSED: int
|
||||
BUTTON5_RELEASED: int
|
||||
BUTTON5_CLICKED: int
|
||||
BUTTON5_DOUBLE_CLICKED: int
|
||||
BUTTON5_TRIPLE_CLICKED: int
|
||||
BUTTON_ALT: int
|
||||
BUTTON_CTRL: int
|
||||
BUTTON_SHIFT: int
|
||||
COLOR_BLACK: int
|
||||
COLOR_BLUE: int
|
||||
COLOR_CYAN: int
|
||||
COLOR_GREEN: int
|
||||
COLOR_MAGENTA: int
|
||||
COLOR_RED: int
|
||||
COLOR_WHITE: int
|
||||
COLOR_YELLOW: int
|
||||
ERR: int
|
||||
KEY_A1: int
|
||||
KEY_A3: int
|
||||
KEY_B2: int
|
||||
KEY_BACKSPACE: int
|
||||
KEY_BEG: int
|
||||
KEY_BREAK: int
|
||||
KEY_BTAB: int
|
||||
KEY_C1: int
|
||||
KEY_C3: int
|
||||
KEY_CANCEL: int
|
||||
KEY_CATAB: int
|
||||
KEY_CLEAR: int
|
||||
KEY_CLOSE: int
|
||||
KEY_COMMAND: int
|
||||
KEY_COPY: int
|
||||
KEY_CREATE: int
|
||||
KEY_CTAB: int
|
||||
KEY_DC: int
|
||||
KEY_DL: int
|
||||
KEY_DOWN: int
|
||||
KEY_EIC: int
|
||||
KEY_END: int
|
||||
KEY_ENTER: int
|
||||
KEY_EOL: int
|
||||
KEY_EOS: int
|
||||
KEY_EXIT: int
|
||||
KEY_F0: int
|
||||
KEY_F1: int
|
||||
KEY_F10: int
|
||||
KEY_F11: int
|
||||
KEY_F12: int
|
||||
KEY_F13: int
|
||||
KEY_F14: int
|
||||
KEY_F15: int
|
||||
KEY_F16: int
|
||||
KEY_F17: int
|
||||
KEY_F18: int
|
||||
KEY_F19: int
|
||||
KEY_F2: int
|
||||
KEY_F20: int
|
||||
KEY_F21: int
|
||||
KEY_F22: int
|
||||
KEY_F23: int
|
||||
KEY_F24: int
|
||||
KEY_F25: int
|
||||
KEY_F26: int
|
||||
KEY_F27: int
|
||||
KEY_F28: int
|
||||
KEY_F29: int
|
||||
KEY_F3: int
|
||||
KEY_F30: int
|
||||
KEY_F31: int
|
||||
KEY_F32: int
|
||||
KEY_F33: int
|
||||
KEY_F34: int
|
||||
KEY_F35: int
|
||||
KEY_F36: int
|
||||
KEY_F37: int
|
||||
KEY_F38: int
|
||||
KEY_F39: int
|
||||
KEY_F4: int
|
||||
KEY_F40: int
|
||||
KEY_F41: int
|
||||
KEY_F42: int
|
||||
KEY_F43: int
|
||||
KEY_F44: int
|
||||
KEY_F45: int
|
||||
KEY_F46: int
|
||||
KEY_F47: int
|
||||
KEY_F48: int
|
||||
KEY_F49: int
|
||||
KEY_F5: int
|
||||
KEY_F50: int
|
||||
KEY_F51: int
|
||||
KEY_F52: int
|
||||
KEY_F53: int
|
||||
KEY_F54: int
|
||||
KEY_F55: int
|
||||
KEY_F56: int
|
||||
KEY_F57: int
|
||||
KEY_F58: int
|
||||
KEY_F59: int
|
||||
KEY_F6: int
|
||||
KEY_F60: int
|
||||
KEY_F61: int
|
||||
KEY_F62: int
|
||||
KEY_F63: int
|
||||
KEY_F7: int
|
||||
KEY_F8: int
|
||||
KEY_F9: int
|
||||
KEY_FIND: int
|
||||
KEY_HELP: int
|
||||
KEY_HOME: int
|
||||
KEY_IC: int
|
||||
KEY_IL: int
|
||||
KEY_LEFT: int
|
||||
KEY_LL: int
|
||||
KEY_MARK: int
|
||||
KEY_MAX: int
|
||||
KEY_MESSAGE: int
|
||||
KEY_MIN: int
|
||||
KEY_MOUSE: int
|
||||
KEY_MOVE: int
|
||||
KEY_NEXT: int
|
||||
KEY_NPAGE: int
|
||||
KEY_OPEN: int
|
||||
KEY_OPTIONS: int
|
||||
KEY_PPAGE: int
|
||||
KEY_PREVIOUS: int
|
||||
KEY_PRINT: int
|
||||
KEY_REDO: int
|
||||
KEY_REFERENCE: int
|
||||
KEY_REFRESH: int
|
||||
KEY_REPLACE: int
|
||||
KEY_RESET: int
|
||||
KEY_RESIZE: int
|
||||
KEY_RESTART: int
|
||||
KEY_RESUME: int
|
||||
KEY_RIGHT: int
|
||||
KEY_SAVE: int
|
||||
KEY_SBEG: int
|
||||
KEY_SCANCEL: int
|
||||
KEY_SCOMMAND: int
|
||||
KEY_SCOPY: int
|
||||
KEY_SCREATE: int
|
||||
KEY_SDC: int
|
||||
KEY_SDL: int
|
||||
KEY_SELECT: int
|
||||
KEY_SEND: int
|
||||
KEY_SEOL: int
|
||||
KEY_SEXIT: int
|
||||
KEY_SF: int
|
||||
KEY_SFIND: int
|
||||
KEY_SHELP: int
|
||||
KEY_SHOME: int
|
||||
KEY_SIC: int
|
||||
KEY_SLEFT: int
|
||||
KEY_SMESSAGE: int
|
||||
KEY_SMOVE: int
|
||||
KEY_SNEXT: int
|
||||
KEY_SOPTIONS: int
|
||||
KEY_SPREVIOUS: int
|
||||
KEY_SPRINT: int
|
||||
KEY_SR: int
|
||||
KEY_SREDO: int
|
||||
KEY_SREPLACE: int
|
||||
KEY_SRESET: int
|
||||
KEY_SRIGHT: int
|
||||
KEY_SRSUME: int
|
||||
KEY_SSAVE: int
|
||||
KEY_SSUSPEND: int
|
||||
KEY_STAB: int
|
||||
KEY_SUNDO: int
|
||||
KEY_SUSPEND: int
|
||||
KEY_UNDO: int
|
||||
KEY_UP: int
|
||||
OK: int
|
||||
REPORT_MOUSE_POSITION: int
|
||||
_C_API: Any
|
||||
version: bytes
|
||||
|
||||
def baudrate() -> int: ...
|
||||
def beep() -> None: ...
|
||||
def can_change_color() -> bool: ...
|
||||
def cbreak(flag: bool = True, /) -> None: ...
|
||||
def color_content(color_number: int, /) -> tuple[int, int, int]: ...
|
||||
def color_pair(pair_number: int, /) -> int: ...
|
||||
def curs_set(visibility: int, /) -> int: ...
|
||||
def def_prog_mode() -> None: ...
|
||||
def def_shell_mode() -> None: ...
|
||||
def delay_output(ms: int, /) -> None: ...
|
||||
def doupdate() -> None: ...
|
||||
def echo(flag: bool = True, /) -> None: ...
|
||||
def endwin() -> None: ...
|
||||
def erasechar() -> bytes: ...
|
||||
def filter() -> None: ...
|
||||
def flash() -> None: ...
|
||||
def flushinp() -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
def get_escdelay() -> int: ...
|
||||
def get_tabsize() -> int: ...
|
||||
|
||||
def getmouse() -> tuple[int, int, int, int, int]: ...
|
||||
def getsyx() -> tuple[int, int]: ...
|
||||
def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ...
|
||||
def halfdelay(tenths: int, /) -> None: ...
|
||||
def has_colors() -> bool: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
def has_extended_color_support() -> bool: ...
|
||||
|
||||
def has_ic() -> bool: ...
|
||||
def has_il() -> bool: ...
|
||||
def has_key(key: int, /) -> bool: ...
|
||||
def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ...
|
||||
def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ...
|
||||
def initscr() -> _CursesWindow: ...
|
||||
def intrflush(flag: bool, /) -> None: ...
|
||||
def is_term_resized(nlines: int, ncols: int, /) -> bool: ...
|
||||
def isendwin() -> bool: ...
|
||||
def keyname(key: int, /) -> bytes: ...
|
||||
def killchar() -> bytes: ...
|
||||
def longname() -> bytes: ...
|
||||
def meta(yes: bool, /) -> None: ...
|
||||
def mouseinterval(interval: int, /) -> None: ...
|
||||
def mousemask(newmask: int, /) -> tuple[int, int]: ...
|
||||
def napms(ms: int, /) -> int: ...
|
||||
def newpad(nlines: int, ncols: int, /) -> _CursesWindow: ...
|
||||
def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> _CursesWindow: ...
|
||||
def nl(flag: bool = True, /) -> None: ...
|
||||
def nocbreak() -> None: ...
|
||||
def noecho() -> None: ...
|
||||
def nonl() -> None: ...
|
||||
def noqiflush() -> None: ...
|
||||
def noraw() -> None: ...
|
||||
def pair_content(pair_number: int, /) -> tuple[int, int]: ...
|
||||
def pair_number(attr: int, /) -> int: ...
|
||||
def putp(string: ReadOnlyBuffer, /) -> None: ...
|
||||
def qiflush(flag: bool = True, /) -> None: ...
|
||||
def raw(flag: bool = True, /) -> None: ...
|
||||
def reset_prog_mode() -> None: ...
|
||||
def reset_shell_mode() -> None: ...
|
||||
def resetty() -> None: ...
|
||||
def resize_term(nlines: int, ncols: int, /) -> None: ...
|
||||
def resizeterm(nlines: int, ncols: int, /) -> None: ...
|
||||
def savetty() -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
def set_escdelay(ms: int, /) -> None: ...
|
||||
def set_tabsize(size: int, /) -> None: ...
|
||||
|
||||
def setsyx(y: int, x: int, /) -> None: ...
|
||||
def setupterm(term: str | None = None, fd: int = -1) -> None: ...
|
||||
def start_color() -> None: ...
|
||||
def termattrs() -> int: ...
|
||||
def termname() -> bytes: ...
|
||||
def tigetflag(capname: str, /) -> int: ...
|
||||
def tigetnum(capname: str, /) -> int: ...
|
||||
def tigetstr(capname: str, /) -> bytes | None: ...
|
||||
def tparm(
|
||||
str: ReadOnlyBuffer,
|
||||
i1: int = 0,
|
||||
i2: int = 0,
|
||||
i3: int = 0,
|
||||
i4: int = 0,
|
||||
i5: int = 0,
|
||||
i6: int = 0,
|
||||
i7: int = 0,
|
||||
i8: int = 0,
|
||||
i9: int = 0,
|
||||
/,
|
||||
) -> 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 ungetch(ch: _ChType, /) -> None: ...
|
||||
def ungetmouse(id: int, x: int, y: int, z: int, bstate: int, /) -> None: ...
|
||||
def update_lines_cols() -> None: ...
|
||||
def use_default_colors() -> None: ...
|
||||
def use_env(flag: bool, /) -> None: ...
|
||||
|
||||
class error(Exception): ...
|
||||
|
||||
@final
|
||||
class _CursesWindow:
|
||||
encoding: str
|
||||
@overload
|
||||
def addch(self, ch: _ChType, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def addch(self, y: int, x: int, ch: _ChType, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def addnstr(self, str: str, n: int, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def addnstr(self, y: int, x: int, str: str, n: int, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def addstr(self, str: str, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def addstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ...
|
||||
def attroff(self, attr: int, /) -> None: ...
|
||||
def attron(self, attr: int, /) -> None: ...
|
||||
def attrset(self, attr: int, /) -> None: ...
|
||||
def bkgd(self, ch: _ChType, attr: int = ..., /) -> None: ...
|
||||
def bkgdset(self, ch: _ChType, attr: int = ..., /) -> None: ...
|
||||
def border(
|
||||
self,
|
||||
ls: _ChType = ...,
|
||||
rs: _ChType = ...,
|
||||
ts: _ChType = ...,
|
||||
bs: _ChType = ...,
|
||||
tl: _ChType = ...,
|
||||
tr: _ChType = ...,
|
||||
bl: _ChType = ...,
|
||||
br: _ChType = ...,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def box(self) -> None: ...
|
||||
@overload
|
||||
def box(self, vertch: _ChType = ..., horch: _ChType = ...) -> None: ...
|
||||
@overload
|
||||
def chgat(self, attr: int) -> None: ...
|
||||
@overload
|
||||
def chgat(self, num: int, attr: int) -> None: ...
|
||||
@overload
|
||||
def chgat(self, y: int, x: int, attr: int) -> None: ...
|
||||
@overload
|
||||
def chgat(self, y: int, x: int, num: int, attr: int) -> None: ...
|
||||
def clear(self) -> None: ...
|
||||
def clearok(self, yes: int) -> None: ...
|
||||
def clrtobot(self) -> None: ...
|
||||
def clrtoeol(self) -> None: ...
|
||||
def cursyncup(self) -> None: ...
|
||||
@overload
|
||||
def delch(self) -> None: ...
|
||||
@overload
|
||||
def delch(self, y: int, x: int) -> None: ...
|
||||
def deleteln(self) -> None: ...
|
||||
@overload
|
||||
def derwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
@overload
|
||||
def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ...
|
||||
def enclose(self, y: int, x: int, /) -> bool: ...
|
||||
def erase(self) -> None: ...
|
||||
def getbegyx(self) -> tuple[int, int]: ...
|
||||
def getbkgd(self) -> tuple[int, int]: ...
|
||||
@overload
|
||||
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 getkey(self) -> str: ...
|
||||
@overload
|
||||
def getkey(self, y: int, x: int) -> str: ...
|
||||
def getmaxyx(self) -> tuple[int, int]: ...
|
||||
def getparyx(self) -> tuple[int, int]: ...
|
||||
@overload
|
||||
def getstr(self) -> bytes: ...
|
||||
@overload
|
||||
def getstr(self, n: int) -> bytes: ...
|
||||
@overload
|
||||
def getstr(self, y: int, x: int) -> bytes: ...
|
||||
@overload
|
||||
def getstr(self, y: int, x: int, n: int) -> bytes: ...
|
||||
def getyx(self) -> tuple[int, int]: ...
|
||||
@overload
|
||||
def hline(self, ch: _ChType, n: int) -> None: ...
|
||||
@overload
|
||||
def hline(self, y: int, x: int, ch: _ChType, n: int) -> None: ...
|
||||
def idcok(self, flag: bool) -> None: ...
|
||||
def idlok(self, yes: bool) -> None: ...
|
||||
def immedok(self, flag: bool) -> None: ...
|
||||
@overload
|
||||
def inch(self) -> int: ...
|
||||
@overload
|
||||
def inch(self, y: int, x: int) -> int: ...
|
||||
@overload
|
||||
def insch(self, ch: _ChType, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def insch(self, y: int, x: int, ch: _ChType, attr: int = ...) -> None: ...
|
||||
def insdelln(self, nlines: int) -> None: ...
|
||||
def insertln(self) -> None: ...
|
||||
@overload
|
||||
def insnstr(self, str: str, n: int, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def insnstr(self, y: int, x: int, str: str, n: int, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def insstr(self, str: str, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def insstr(self, y: int, x: int, str: str, attr: int = ...) -> None: ...
|
||||
@overload
|
||||
def instr(self, n: int = ...) -> bytes: ...
|
||||
@overload
|
||||
def instr(self, y: int, x: int, n: int = ...) -> bytes: ...
|
||||
def is_linetouched(self, line: int, /) -> bool: ...
|
||||
def is_wintouched(self) -> bool: ...
|
||||
def keypad(self, yes: bool) -> None: ...
|
||||
def leaveok(self, yes: bool) -> None: ...
|
||||
def move(self, new_y: int, new_x: int) -> None: ...
|
||||
def mvderwin(self, y: int, x: int) -> None: ...
|
||||
def mvwin(self, new_y: int, new_x: int) -> None: ...
|
||||
def nodelay(self, yes: bool) -> None: ...
|
||||
def notimeout(self, yes: bool) -> None: ...
|
||||
@overload
|
||||
def noutrefresh(self) -> None: ...
|
||||
@overload
|
||||
def noutrefresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ...
|
||||
@overload
|
||||
def overlay(self, destwin: _CursesWindow) -> None: ...
|
||||
@overload
|
||||
def overlay(
|
||||
self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int
|
||||
) -> None: ...
|
||||
@overload
|
||||
def overwrite(self, destwin: _CursesWindow) -> None: ...
|
||||
@overload
|
||||
def overwrite(
|
||||
self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int
|
||||
) -> None: ...
|
||||
def putwin(self, file: IO[Any], /) -> None: ...
|
||||
def redrawln(self, beg: int, num: int, /) -> None: ...
|
||||
def redrawwin(self) -> None: ...
|
||||
@overload
|
||||
def refresh(self) -> None: ...
|
||||
@overload
|
||||
def refresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ...
|
||||
def resize(self, nlines: int, ncols: int) -> None: ...
|
||||
def scroll(self, lines: int = ...) -> None: ...
|
||||
def scrollok(self, flag: bool) -> None: ...
|
||||
def setscrreg(self, top: int, bottom: int, /) -> None: ...
|
||||
def standend(self) -> None: ...
|
||||
def standout(self) -> None: ...
|
||||
@overload
|
||||
def subpad(self, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
@overload
|
||||
def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
@overload
|
||||
def subwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
@overload
|
||||
def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ...
|
||||
def syncdown(self) -> None: ...
|
||||
def syncok(self, flag: bool) -> None: ...
|
||||
def syncup(self) -> None: ...
|
||||
def timeout(self, delay: int) -> None: ...
|
||||
def touchline(self, start: int, count: int, changed: bool = ...) -> None: ...
|
||||
def touchwin(self) -> None: ...
|
||||
def untouchwin(self) -> None: ...
|
||||
@overload
|
||||
def vline(self, ch: _ChType, n: int) -> None: ...
|
||||
@overload
|
||||
def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ...
|
||||
|
||||
class _ncurses_version(NamedTuple):
|
||||
major: int
|
||||
minor: int
|
||||
patch: int
|
||||
|
||||
ncurses_version: _ncurses_version
|
||||
window = _CursesWindow # undocumented
|
||||
@@ -1,281 +0,0 @@
|
||||
import numbers
|
||||
import sys
|
||||
from collections.abc import Container, Sequence
|
||||
from types import TracebackType
|
||||
from typing import Any, ClassVar, Final, Literal, NamedTuple, overload
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
_Decimal: TypeAlias = Decimal | int
|
||||
_DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int]
|
||||
_ComparableNum: TypeAlias = Decimal | float | numbers.Rational
|
||||
|
||||
__version__: Final[str]
|
||||
__libmpdec_version__: Final[str]
|
||||
|
||||
class DecimalTuple(NamedTuple):
|
||||
sign: int
|
||||
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
|
||||
|
||||
class DecimalException(ArithmeticError): ...
|
||||
class Clamped(DecimalException): ...
|
||||
class InvalidOperation(DecimalException): ...
|
||||
class ConversionSyntax(InvalidOperation): ...
|
||||
class DivisionByZero(DecimalException, ZeroDivisionError): ...
|
||||
class DivisionImpossible(InvalidOperation): ...
|
||||
class DivisionUndefined(InvalidOperation, ZeroDivisionError): ...
|
||||
class Inexact(DecimalException): ...
|
||||
class InvalidContext(InvalidOperation): ...
|
||||
class Rounded(DecimalException): ...
|
||||
class Subnormal(DecimalException): ...
|
||||
class Overflow(Inexact, Rounded): ...
|
||||
class Underflow(Inexact, Rounded, Subnormal): ...
|
||||
class FloatOperation(DecimalException, TypeError): ...
|
||||
|
||||
def setcontext(context: Context, /) -> None: ...
|
||||
def getcontext() -> Context: ...
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
def localcontext(
|
||||
ctx: Context | None = None,
|
||||
*,
|
||||
prec: int | None = ...,
|
||||
rounding: str | None = ...,
|
||||
Emin: int | None = ...,
|
||||
Emax: int | None = ...,
|
||||
capitals: int | None = ...,
|
||||
clamp: int | None = ...,
|
||||
traps: dict[_TrapType, bool] | None = ...,
|
||||
flags: dict[_TrapType, bool] | None = ...,
|
||||
) -> _ContextManager: ...
|
||||
|
||||
else:
|
||||
def localcontext(ctx: Context | None = None) -> _ContextManager: ...
|
||||
|
||||
class Decimal:
|
||||
def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_float(cls, f: float, /) -> Self: ...
|
||||
def __bool__(self) -> bool: ...
|
||||
def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __hash__(self) -> int: ...
|
||||
def as_tuple(self) -> DecimalTuple: ...
|
||||
def as_integer_ratio(self) -> tuple[int, int]: ...
|
||||
def to_eng_string(self, context: Context | None = None) -> str: ...
|
||||
def __abs__(self) -> Decimal: ...
|
||||
def __add__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ...
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __floordiv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __ge__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __gt__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __le__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __lt__(self, value: _ComparableNum, /) -> bool: ...
|
||||
def __mod__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __mul__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __neg__(self) -> Decimal: ...
|
||||
def __pos__(self) -> Decimal: ...
|
||||
def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ...
|
||||
def __radd__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ...
|
||||
def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rmod__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rmul__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rsub__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __rtruediv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __sub__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def __truediv__(self, value: _Decimal, /) -> Decimal: ...
|
||||
def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __float__(self) -> float: ...
|
||||
def __int__(self) -> int: ...
|
||||
def __trunc__(self) -> int: ...
|
||||
@property
|
||||
def real(self) -> Decimal: ...
|
||||
@property
|
||||
def imag(self) -> Decimal: ...
|
||||
def conjugate(self) -> Decimal: ...
|
||||
def __complex__(self) -> complex: ...
|
||||
@overload
|
||||
def __round__(self) -> int: ...
|
||||
@overload
|
||||
def __round__(self, ndigits: int, /) -> Decimal: ...
|
||||
def __floor__(self) -> int: ...
|
||||
def __ceil__(self) -> int: ...
|
||||
def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ...
|
||||
def normalize(self, context: Context | None = None) -> Decimal: ...
|
||||
def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ...
|
||||
def to_integral_exact(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def to_integral_value(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def to_integral(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ...
|
||||
def sqrt(self, context: Context | None = None) -> Decimal: ...
|
||||
def max(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def min(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def adjusted(self) -> int: ...
|
||||
def canonical(self) -> Decimal: ...
|
||||
def compare_signal(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def compare_total(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def compare_total_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def copy_abs(self) -> Decimal: ...
|
||||
def copy_negate(self) -> Decimal: ...
|
||||
def copy_sign(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def exp(self, context: Context | None = None) -> Decimal: ...
|
||||
def is_canonical(self) -> bool: ...
|
||||
def is_finite(self) -> bool: ...
|
||||
def is_infinite(self) -> bool: ...
|
||||
def is_nan(self) -> bool: ...
|
||||
def is_normal(self, context: Context | None = None) -> bool: ...
|
||||
def is_qnan(self) -> bool: ...
|
||||
def is_signed(self) -> bool: ...
|
||||
def is_snan(self) -> bool: ...
|
||||
def is_subnormal(self, context: Context | None = None) -> bool: ...
|
||||
def is_zero(self) -> bool: ...
|
||||
def ln(self, context: Context | None = None) -> Decimal: ...
|
||||
def log10(self, context: Context | None = None) -> Decimal: ...
|
||||
def logb(self, context: Context | None = None) -> Decimal: ...
|
||||
def logical_and(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def logical_invert(self, context: Context | None = None) -> Decimal: ...
|
||||
def logical_or(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def logical_xor(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def max_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def min_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def next_minus(self, context: Context | None = None) -> Decimal: ...
|
||||
def next_plus(self, context: Context | None = None) -> Decimal: ...
|
||||
def next_toward(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def number_class(self, context: Context | None = None) -> str: ...
|
||||
def radix(self) -> Decimal: ...
|
||||
def rotate(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def scaleb(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ...
|
||||
def __reduce__(self) -> tuple[type[Self], tuple[str]]: ...
|
||||
def __copy__(self) -> Self: ...
|
||||
def __deepcopy__(self, memo: Any, /) -> Self: ...
|
||||
def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ...
|
||||
|
||||
class _ContextManager:
|
||||
new_context: Context
|
||||
saved_context: Context
|
||||
def __init__(self, new_context: Context) -> None: ...
|
||||
def __enter__(self) -> Context: ...
|
||||
def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
|
||||
_TrapType: TypeAlias = type[DecimalException]
|
||||
|
||||
class Context:
|
||||
# TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime,
|
||||
# even settable attributes like `prec` and `rounding`,
|
||||
# but that's inexpressable in the stub.
|
||||
# Type checkers either ignore it or misinterpret it
|
||||
# if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub
|
||||
prec: int
|
||||
rounding: str
|
||||
Emin: int
|
||||
Emax: int
|
||||
capitals: int
|
||||
clamp: int
|
||||
traps: dict[_TrapType, bool]
|
||||
flags: dict[_TrapType, bool]
|
||||
def __init__(
|
||||
self,
|
||||
prec: int | None = ...,
|
||||
rounding: str | None = ...,
|
||||
Emin: int | None = ...,
|
||||
Emax: int | None = ...,
|
||||
capitals: int | None = ...,
|
||||
clamp: int | None = ...,
|
||||
flags: None | dict[_TrapType, bool] | Container[_TrapType] = ...,
|
||||
traps: None | dict[_TrapType, bool] | Container[_TrapType] = ...,
|
||||
_ignored_flags: list[_TrapType] | None = ...,
|
||||
) -> None: ...
|
||||
def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ...
|
||||
def clear_flags(self) -> None: ...
|
||||
def clear_traps(self) -> None: ...
|
||||
def copy(self) -> Context: ...
|
||||
def __copy__(self) -> Context: ...
|
||||
# see https://github.com/python/cpython/issues/94107
|
||||
__hash__: ClassVar[None] # type: ignore[assignment]
|
||||
def Etiny(self) -> int: ...
|
||||
def Etop(self) -> int: ...
|
||||
def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ...
|
||||
def create_decimal_from_float(self, f: float, /) -> Decimal: ...
|
||||
def abs(self, x: _Decimal, /) -> Decimal: ...
|
||||
def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def canonical(self, x: Decimal, /) -> Decimal: ...
|
||||
def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def copy_abs(self, x: _Decimal, /) -> Decimal: ...
|
||||
def copy_decimal(self, x: _Decimal, /) -> Decimal: ...
|
||||
def copy_negate(self, x: _Decimal, /) -> Decimal: ...
|
||||
def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ...
|
||||
def exp(self, x: _Decimal, /) -> Decimal: ...
|
||||
def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ...
|
||||
def is_canonical(self, x: _Decimal, /) -> bool: ...
|
||||
def is_finite(self, x: _Decimal, /) -> bool: ...
|
||||
def is_infinite(self, x: _Decimal, /) -> bool: ...
|
||||
def is_nan(self, x: _Decimal, /) -> bool: ...
|
||||
def is_normal(self, x: _Decimal, /) -> bool: ...
|
||||
def is_qnan(self, x: _Decimal, /) -> bool: ...
|
||||
def is_signed(self, x: _Decimal, /) -> bool: ...
|
||||
def is_snan(self, x: _Decimal, /) -> bool: ...
|
||||
def is_subnormal(self, x: _Decimal, /) -> bool: ...
|
||||
def is_zero(self, x: _Decimal, /) -> bool: ...
|
||||
def ln(self, x: _Decimal, /) -> Decimal: ...
|
||||
def log10(self, x: _Decimal, /) -> Decimal: ...
|
||||
def logb(self, x: _Decimal, /) -> Decimal: ...
|
||||
def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def logical_invert(self, x: _Decimal, /) -> Decimal: ...
|
||||
def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def minus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def next_minus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def next_plus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def normalize(self, x: _Decimal, /) -> Decimal: ...
|
||||
def number_class(self, x: _Decimal, /) -> str: ...
|
||||
def plus(self, x: _Decimal, /) -> Decimal: ...
|
||||
def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ...
|
||||
def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def radix(self) -> Decimal: ...
|
||||
def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ...
|
||||
def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def sqrt(self, x: _Decimal, /) -> Decimal: ...
|
||||
def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ...
|
||||
def to_eng_string(self, x: _Decimal, /) -> str: ...
|
||||
def to_sci_string(self, x: _Decimal, /) -> str: ...
|
||||
def to_integral_exact(self, x: _Decimal, /) -> Decimal: ...
|
||||
def to_integral_value(self, x: _Decimal, /) -> Decimal: ...
|
||||
def to_integral(self, x: _Decimal, /) -> Decimal: ...
|
||||
|
||||
DefaultContext: Context
|
||||
BasicContext: Context
|
||||
ExtendedContext: Context
|
||||
@@ -1,33 +0,0 @@
|
||||
from collections.abc import Callable
|
||||
from types import TracebackType
|
||||
from typing import Any, NoReturn, overload
|
||||
from typing_extensions import TypeVarTuple, Unpack
|
||||
|
||||
__all__ = ["error", "start_new_thread", "exit", "get_ident", "allocate_lock", "interrupt_main", "LockType", "RLock"]
|
||||
|
||||
_Ts = TypeVarTuple("_Ts")
|
||||
|
||||
TIMEOUT_MAX: int
|
||||
error = RuntimeError
|
||||
|
||||
@overload
|
||||
def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> None: ...
|
||||
@overload
|
||||
def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> None: ...
|
||||
def exit() -> NoReturn: ...
|
||||
def get_ident() -> int: ...
|
||||
def allocate_lock() -> LockType: ...
|
||||
def stack_size(size: int | None = None) -> int: ...
|
||||
|
||||
class LockType:
|
||||
locked_status: bool
|
||||
def acquire(self, waitflag: bool | None = None, timeout: int = -1) -> bool: ...
|
||||
def __enter__(self, waitflag: bool | None = None, timeout: int = -1) -> bool: ...
|
||||
def __exit__(self, typ: type[BaseException] | None, val: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
def release(self) -> bool: ...
|
||||
def locked(self) -> bool: ...
|
||||
|
||||
class RLock(LockType):
|
||||
def release(self) -> None: ... # type: ignore[override]
|
||||
|
||||
def interrupt_main() -> None: ...
|
||||
@@ -1,164 +0,0 @@
|
||||
import sys
|
||||
from _thread import _excepthook, _ExceptHookArgs
|
||||
from _typeshed import ProfileFunction, TraceFunction
|
||||
from collections.abc import Callable, Iterable, Mapping
|
||||
from types import TracebackType
|
||||
from typing import Any, TypeVar
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
__all__ = [
|
||||
"get_ident",
|
||||
"active_count",
|
||||
"Condition",
|
||||
"current_thread",
|
||||
"enumerate",
|
||||
"main_thread",
|
||||
"TIMEOUT_MAX",
|
||||
"Event",
|
||||
"Lock",
|
||||
"RLock",
|
||||
"Semaphore",
|
||||
"BoundedSemaphore",
|
||||
"Thread",
|
||||
"Barrier",
|
||||
"BrokenBarrierError",
|
||||
"Timer",
|
||||
"ThreadError",
|
||||
"setprofile",
|
||||
"settrace",
|
||||
"local",
|
||||
"stack_size",
|
||||
"ExceptHookArgs",
|
||||
"excepthook",
|
||||
]
|
||||
|
||||
def active_count() -> int: ...
|
||||
def current_thread() -> Thread: ...
|
||||
def currentThread() -> Thread: ...
|
||||
def get_ident() -> int: ...
|
||||
def enumerate() -> list[Thread]: ...
|
||||
def main_thread() -> Thread: ...
|
||||
def settrace(func: TraceFunction) -> None: ...
|
||||
def setprofile(func: ProfileFunction | None) -> None: ...
|
||||
def stack_size(size: int | None = None) -> int: ...
|
||||
|
||||
TIMEOUT_MAX: float
|
||||
|
||||
class ThreadError(Exception): ...
|
||||
|
||||
class local:
|
||||
def __getattribute__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
def __delattr__(self, name: str) -> None: ...
|
||||
|
||||
class Thread:
|
||||
name: str
|
||||
daemon: bool
|
||||
@property
|
||||
def ident(self) -> int | None: ...
|
||||
def __init__(
|
||||
self,
|
||||
group: None = None,
|
||||
target: Callable[..., object] | None = None,
|
||||
name: str | None = None,
|
||||
args: Iterable[Any] = (),
|
||||
kwargs: Mapping[str, Any] | None = None,
|
||||
*,
|
||||
daemon: bool | None = None,
|
||||
) -> None: ...
|
||||
def start(self) -> None: ...
|
||||
def run(self) -> None: ...
|
||||
def join(self, timeout: float | None = None) -> None: ...
|
||||
def getName(self) -> str: ...
|
||||
def setName(self, name: str) -> None: ...
|
||||
@property
|
||||
def native_id(self) -> int | None: ... # only available on some platforms
|
||||
def is_alive(self) -> bool: ...
|
||||
if sys.version_info < (3, 9):
|
||||
def isAlive(self) -> bool: ...
|
||||
|
||||
def isDaemon(self) -> bool: ...
|
||||
def setDaemon(self, daemonic: bool) -> None: ...
|
||||
|
||||
class _DummyThread(Thread): ...
|
||||
|
||||
class Lock:
|
||||
def __enter__(self) -> bool: ...
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> bool | None: ...
|
||||
def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ...
|
||||
def release(self) -> None: ...
|
||||
def locked(self) -> bool: ...
|
||||
|
||||
class _RLock:
|
||||
def __enter__(self) -> bool: ...
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> bool | None: ...
|
||||
def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ...
|
||||
def release(self) -> None: ...
|
||||
|
||||
RLock = _RLock
|
||||
|
||||
class Condition:
|
||||
def __init__(self, lock: Lock | _RLock | None = None) -> None: ...
|
||||
def __enter__(self) -> bool: ...
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> bool | None: ...
|
||||
def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ...
|
||||
def release(self) -> None: ...
|
||||
def wait(self, timeout: float | None = None) -> bool: ...
|
||||
def wait_for(self, predicate: Callable[[], _T], timeout: float | None = None) -> _T: ...
|
||||
def notify(self, n: int = 1) -> None: ...
|
||||
def notify_all(self) -> None: ...
|
||||
def notifyAll(self) -> None: ...
|
||||
|
||||
class Semaphore:
|
||||
def __init__(self, value: int = 1) -> None: ...
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> bool | None: ...
|
||||
def acquire(self, blocking: bool = True, timeout: float | None = None) -> bool: ...
|
||||
def __enter__(self, blocking: bool = True, timeout: float | None = None) -> bool: ...
|
||||
if sys.version_info >= (3, 9):
|
||||
def release(self, n: int = ...) -> None: ...
|
||||
else:
|
||||
def release(self) -> None: ...
|
||||
|
||||
class BoundedSemaphore(Semaphore): ...
|
||||
|
||||
class Event:
|
||||
def is_set(self) -> bool: ...
|
||||
def set(self) -> None: ...
|
||||
def clear(self) -> None: ...
|
||||
def wait(self, timeout: float | None = None) -> bool: ...
|
||||
|
||||
excepthook = _excepthook
|
||||
ExceptHookArgs = _ExceptHookArgs
|
||||
|
||||
class Timer(Thread):
|
||||
def __init__(
|
||||
self,
|
||||
interval: float,
|
||||
function: Callable[..., object],
|
||||
args: Iterable[Any] | None = None,
|
||||
kwargs: Mapping[str, Any] | None = None,
|
||||
) -> None: ...
|
||||
def cancel(self) -> None: ...
|
||||
|
||||
class Barrier:
|
||||
@property
|
||||
def parties(self) -> int: ...
|
||||
@property
|
||||
def n_waiting(self) -> int: ...
|
||||
@property
|
||||
def broken(self) -> bool: ...
|
||||
def __init__(self, parties: int, action: Callable[[], None] | None = None, timeout: float | None = None) -> None: ...
|
||||
def wait(self, timeout: float | None = None) -> int: ...
|
||||
def reset(self) -> None: ...
|
||||
def abort(self) -> None: ...
|
||||
|
||||
class BrokenBarrierError(RuntimeError): ...
|
||||
@@ -1,11 +0,0 @@
|
||||
from typing import Any, Final, TypeVar
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
__about__: Final[str]
|
||||
|
||||
def heapify(heap: list[Any], /) -> None: ...
|
||||
def heappop(heap: list[_T], /) -> _T: ...
|
||||
def heappush(heap: list[_T], item: _T, /) -> None: ...
|
||||
def heappushpop(heap: list[_T], item: _T, /) -> _T: ...
|
||||
def heapreplace(heap: list[_T], item: _T, /) -> _T: ...
|
||||
@@ -1,28 +0,0 @@
|
||||
import sys
|
||||
import types
|
||||
from _typeshed import ReadableBuffer
|
||||
from importlib.machinery import ModuleSpec
|
||||
from typing import Any
|
||||
|
||||
check_hash_based_pycs: str
|
||||
|
||||
def source_hash(key: int, source: ReadableBuffer) -> bytes: ...
|
||||
def create_builtin(spec: ModuleSpec, /) -> types.ModuleType: ...
|
||||
def create_dynamic(spec: ModuleSpec, file: Any = None, /) -> types.ModuleType: ...
|
||||
def acquire_lock() -> None: ...
|
||||
def exec_builtin(mod: types.ModuleType, /) -> int: ...
|
||||
def exec_dynamic(mod: types.ModuleType, /) -> int: ...
|
||||
def extension_suffixes() -> list[str]: ...
|
||||
def init_frozen(name: str, /) -> types.ModuleType: ...
|
||||
def is_builtin(name: str, /) -> int: ...
|
||||
def is_frozen(name: str, /) -> bool: ...
|
||||
def is_frozen_package(name: str, /) -> bool: ...
|
||||
def lock_held() -> bool: ...
|
||||
def release_lock() -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
def find_frozen(name: str, /, *, withdata: bool = False) -> tuple[memoryview | None, bool, str | None] | None: ...
|
||||
def get_frozen_object(name: str, data: ReadableBuffer | None = None, /) -> types.CodeType: ...
|
||||
|
||||
else:
|
||||
def get_frozen_object(name: str, /) -> types.CodeType: ...
|
||||
@@ -1,84 +0,0 @@
|
||||
from _typeshed import structseq
|
||||
from typing import Final, Literal, SupportsIndex, final
|
||||
from typing_extensions import Buffer, Self
|
||||
|
||||
class ChannelError(RuntimeError): ...
|
||||
class ChannelClosedError(ChannelError): ...
|
||||
class ChannelEmptyError(ChannelError): ...
|
||||
class ChannelNotEmptyError(ChannelError): ...
|
||||
class ChannelNotFoundError(ChannelError): ...
|
||||
|
||||
# Mark as final, since instantiating ChannelID is not supported.
|
||||
@final
|
||||
class ChannelID:
|
||||
@property
|
||||
def end(self) -> Literal["send", "recv", "both"]: ...
|
||||
@property
|
||||
def send(self) -> Self: ...
|
||||
@property
|
||||
def recv(self) -> Self: ...
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
def __ge__(self, other: ChannelID) -> bool: ...
|
||||
def __gt__(self, other: ChannelID) -> bool: ...
|
||||
def __hash__(self) -> int: ...
|
||||
def __index__(self) -> int: ...
|
||||
def __int__(self) -> int: ...
|
||||
def __le__(self, other: ChannelID) -> bool: ...
|
||||
def __lt__(self, other: ChannelID) -> bool: ...
|
||||
def __ne__(self, other: object) -> bool: ...
|
||||
|
||||
@final
|
||||
class ChannelInfo(structseq[int], tuple[bool, bool, bool, int, int, int, int, int]):
|
||||
__match_args__: Final = (
|
||||
"open",
|
||||
"closing",
|
||||
"closed",
|
||||
"count",
|
||||
"num_interp_send",
|
||||
"num_interp_send_released",
|
||||
"num_interp_recv",
|
||||
"num_interp_recv_released",
|
||||
)
|
||||
@property
|
||||
def open(self) -> bool: ...
|
||||
@property
|
||||
def closing(self) -> bool: ...
|
||||
@property
|
||||
def closed(self) -> bool: ...
|
||||
@property
|
||||
def count(self) -> int: ... # type: ignore[override]
|
||||
@property
|
||||
def num_interp_send(self) -> int: ...
|
||||
@property
|
||||
def num_interp_send_released(self) -> int: ...
|
||||
@property
|
||||
def num_interp_recv(self) -> int: ...
|
||||
@property
|
||||
def num_interp_recv_released(self) -> int: ...
|
||||
@property
|
||||
def num_interp_both(self) -> int: ...
|
||||
@property
|
||||
def num_interp_both_recv_released(self) -> int: ...
|
||||
@property
|
||||
def num_interp_both_send_released(self) -> int: ...
|
||||
@property
|
||||
def num_interp_both_released(self) -> int: ...
|
||||
@property
|
||||
def recv_associated(self) -> bool: ...
|
||||
@property
|
||||
def recv_released(self) -> bool: ...
|
||||
@property
|
||||
def send_associated(self) -> bool: ...
|
||||
@property
|
||||
def send_released(self) -> bool: ...
|
||||
|
||||
def create() -> 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 close(cid: SupportsIndex, *, send: bool = False, recv: bool = False) -> None: ...
|
||||
def get_info(cid: SupportsIndex) -> ChannelInfo: ...
|
||||
def release(cid: SupportsIndex, *, send: bool = False, recv: bool = False, force: bool = False) -> None: ...
|
||||
@@ -1,49 +0,0 @@
|
||||
from collections.abc import Callable
|
||||
from typing import Any, final
|
||||
|
||||
@final
|
||||
class make_encoder:
|
||||
@property
|
||||
def sort_keys(self) -> bool: ...
|
||||
@property
|
||||
def skipkeys(self) -> bool: ...
|
||||
@property
|
||||
def key_separator(self) -> str: ...
|
||||
@property
|
||||
def indent(self) -> int | None: ...
|
||||
@property
|
||||
def markers(self) -> dict[int, Any] | None: ...
|
||||
@property
|
||||
def default(self) -> Callable[[Any], Any]: ...
|
||||
@property
|
||||
def encoder(self) -> Callable[[str], str]: ...
|
||||
@property
|
||||
def item_separator(self) -> str: ...
|
||||
def __init__(
|
||||
self,
|
||||
markers: dict[int, Any] | None,
|
||||
default: Callable[[Any], Any],
|
||||
encoder: Callable[[str], str],
|
||||
indent: int | None,
|
||||
key_separator: str,
|
||||
item_separator: str,
|
||||
sort_keys: bool,
|
||||
skipkeys: bool,
|
||||
allow_nan: bool,
|
||||
) -> None: ...
|
||||
def __call__(self, obj: object, _current_indent_level: int) -> Any: ...
|
||||
|
||||
@final
|
||||
class make_scanner:
|
||||
object_hook: Any
|
||||
object_pairs_hook: Any
|
||||
parse_int: Any
|
||||
parse_constant: Any
|
||||
parse_float: Any
|
||||
strict: bool
|
||||
# TODO: 'context' needs the attrs above (ducktype), but not __call__.
|
||||
def __init__(self, context: make_scanner) -> None: ...
|
||||
def __call__(self, string: str, index: int) -> tuple[Any, int]: ...
|
||||
|
||||
def encode_basestring_ascii(s: str, /) -> str: ...
|
||||
def scanstring(string: str, end: int, strict: bool = ...) -> tuple[str, int]: ...
|
||||
@@ -1,100 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import StrPath
|
||||
from collections.abc import Mapping
|
||||
|
||||
LC_CTYPE: int
|
||||
LC_COLLATE: int
|
||||
LC_TIME: int
|
||||
LC_MONETARY: int
|
||||
LC_NUMERIC: int
|
||||
LC_ALL: int
|
||||
CHAR_MAX: int
|
||||
|
||||
def setlocale(category: int, locale: str | None = None, /) -> str: ...
|
||||
def localeconv() -> Mapping[str, int | str | list[int]]: ...
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
def getencoding() -> str: ...
|
||||
|
||||
def strcoll(os1: str, os2: str, /) -> int: ...
|
||||
def strxfrm(string: str, /) -> str: ...
|
||||
|
||||
# native gettext functions
|
||||
# https://docs.python.org/3/library/locale.html#access-to-message-catalogs
|
||||
# https://github.com/python/cpython/blob/f4c03484da59049eb62a9bf7777b963e2267d187/Modules/_localemodule.c#L626
|
||||
if sys.platform != "win32":
|
||||
LC_MESSAGES: int
|
||||
|
||||
ABDAY_1: int
|
||||
ABDAY_2: int
|
||||
ABDAY_3: int
|
||||
ABDAY_4: int
|
||||
ABDAY_5: int
|
||||
ABDAY_6: int
|
||||
ABDAY_7: int
|
||||
|
||||
ABMON_1: int
|
||||
ABMON_2: int
|
||||
ABMON_3: int
|
||||
ABMON_4: int
|
||||
ABMON_5: int
|
||||
ABMON_6: int
|
||||
ABMON_7: int
|
||||
ABMON_8: int
|
||||
ABMON_9: int
|
||||
ABMON_10: int
|
||||
ABMON_11: int
|
||||
ABMON_12: int
|
||||
|
||||
DAY_1: int
|
||||
DAY_2: int
|
||||
DAY_3: int
|
||||
DAY_4: int
|
||||
DAY_5: int
|
||||
DAY_6: int
|
||||
DAY_7: int
|
||||
|
||||
ERA: int
|
||||
ERA_D_T_FMT: int
|
||||
ERA_D_FMT: int
|
||||
ERA_T_FMT: int
|
||||
|
||||
MON_1: int
|
||||
MON_2: int
|
||||
MON_3: int
|
||||
MON_4: int
|
||||
MON_5: int
|
||||
MON_6: int
|
||||
MON_7: int
|
||||
MON_8: int
|
||||
MON_9: int
|
||||
MON_10: int
|
||||
MON_11: int
|
||||
MON_12: int
|
||||
|
||||
CODESET: int
|
||||
D_T_FMT: int
|
||||
D_FMT: int
|
||||
T_FMT: int
|
||||
T_FMT_AMPM: int
|
||||
AM_STR: int
|
||||
PM_STR: int
|
||||
|
||||
RADIXCHAR: int
|
||||
THOUSEP: int
|
||||
YESEXPR: int
|
||||
NOEXPR: int
|
||||
CRNCYSTR: int
|
||||
ALT_DIGITS: int
|
||||
|
||||
def nl_langinfo(key: int, /) -> str: ...
|
||||
|
||||
# This is dependent on `libintl.h` which is a part of `gettext`
|
||||
# system dependency. These functions might be missing.
|
||||
# But, we always say that they are present.
|
||||
def gettext(msg: str, /) -> str: ...
|
||||
def dgettext(domain: str | None, msg: str, /) -> str: ...
|
||||
def dcgettext(domain: str | None, msg: str, category: int, /) -> str: ...
|
||||
def textdomain(domain: str | None, /) -> str: ...
|
||||
def bindtextdomain(domain: str, dir: StrPath | None, /) -> str: ...
|
||||
def bind_textdomain_codeset(domain: str, codeset: str | None, /) -> str | None: ...
|
||||
@@ -1,35 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import structseq
|
||||
from collections.abc import Callable
|
||||
from types import CodeType
|
||||
from typing import Any, Final, final
|
||||
|
||||
class Profiler:
|
||||
def __init__(
|
||||
self, timer: Callable[[], float] | None = None, timeunit: float = 0.0, subcalls: bool = True, builtins: bool = True
|
||||
) -> None: ...
|
||||
def getstats(self) -> list[profiler_entry]: ...
|
||||
def enable(self, subcalls: bool = True, builtins: bool = True) -> None: ...
|
||||
def disable(self) -> None: ...
|
||||
def clear(self) -> None: ...
|
||||
|
||||
@final
|
||||
class profiler_entry(structseq[Any], tuple[CodeType | str, int, int, float, float, list[profiler_subentry]]):
|
||||
if sys.version_info >= (3, 10):
|
||||
__match_args__: Final = ("code", "callcount", "reccallcount", "totaltime", "inlinetime", "calls")
|
||||
code: CodeType | str
|
||||
callcount: int
|
||||
reccallcount: int
|
||||
totaltime: float
|
||||
inlinetime: float
|
||||
calls: list[profiler_subentry]
|
||||
|
||||
@final
|
||||
class profiler_subentry(structseq[Any], tuple[CodeType | str, int, int, float, float]):
|
||||
if sys.version_info >= (3, 10):
|
||||
__match_args__: Final = ("code", "callcount", "reccallcount", "totaltime", "inlinetime")
|
||||
code: CodeType | str
|
||||
callcount: int
|
||||
reccallcount: int
|
||||
totaltime: float
|
||||
inlinetime: float
|
||||
@@ -1,16 +0,0 @@
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
class ParserBase:
|
||||
def reset(self) -> None: ...
|
||||
def getpos(self) -> tuple[int, int]: ...
|
||||
def unknown_decl(self, data: str) -> None: ...
|
||||
def parse_comment(self, i: int, report: int = 1) -> int: ... # undocumented
|
||||
def parse_declaration(self, i: int) -> int: ... # undocumented
|
||||
def parse_marked_section(self, i: int, report: int = 1) -> int: ... # undocumented
|
||||
def updatepos(self, i: int, j: int) -> int: ... # undocumented
|
||||
if sys.version_info < (3, 10):
|
||||
# Removed from ParserBase: https://bugs.python.org/issue31844
|
||||
def error(self, message: str) -> Any: ... # undocumented
|
||||
lineno: int # undocumented
|
||||
offset: int # undocumented
|
||||
@@ -1,92 +0,0 @@
|
||||
import sys
|
||||
|
||||
if sys.platform == "win32":
|
||||
class MSIError(Exception): ...
|
||||
# Actual typename View, not exposed by the implementation
|
||||
class _View:
|
||||
def Execute(self, params: _Record | None = ...) -> None: ...
|
||||
def GetColumnInfo(self, kind: int) -> _Record: ...
|
||||
def Fetch(self) -> _Record: ...
|
||||
def Modify(self, mode: int, record: _Record) -> None: ...
|
||||
def Close(self) -> None: ...
|
||||
# Don't exist at runtime
|
||||
__new__: None # type: ignore[assignment]
|
||||
__init__: None # type: ignore[assignment]
|
||||
|
||||
# Actual typename SummaryInformation, not exposed by the implementation
|
||||
class _SummaryInformation:
|
||||
def GetProperty(self, field: int) -> int | bytes | None: ...
|
||||
def GetPropertyCount(self) -> int: ...
|
||||
def SetProperty(self, field: int, value: int | str) -> None: ...
|
||||
def Persist(self) -> None: ...
|
||||
# Don't exist at runtime
|
||||
__new__: None # type: ignore[assignment]
|
||||
__init__: None # type: ignore[assignment]
|
||||
|
||||
# Actual typename Database, not exposed by the implementation
|
||||
class _Database:
|
||||
def OpenView(self, sql: str) -> _View: ...
|
||||
def Commit(self) -> None: ...
|
||||
def GetSummaryInformation(self, updateCount: int) -> _SummaryInformation: ...
|
||||
def Close(self) -> None: ...
|
||||
# Don't exist at runtime
|
||||
__new__: None # type: ignore[assignment]
|
||||
__init__: None # type: ignore[assignment]
|
||||
|
||||
# Actual typename Record, not exposed by the implementation
|
||||
class _Record:
|
||||
def GetFieldCount(self) -> int: ...
|
||||
def GetInteger(self, field: int) -> int: ...
|
||||
def GetString(self, field: int) -> str: ...
|
||||
def SetString(self, field: int, str: str) -> None: ...
|
||||
def SetStream(self, field: int, stream: str) -> None: ...
|
||||
def SetInteger(self, field: int, int: int) -> None: ...
|
||||
def ClearData(self) -> None: ...
|
||||
# Don't exist at runtime
|
||||
__new__: None # type: ignore[assignment]
|
||||
__init__: None # type: ignore[assignment]
|
||||
|
||||
def UuidCreate() -> str: ...
|
||||
def FCICreate(cabname: str, files: list[str], /) -> None: ...
|
||||
def OpenDatabase(path: str, persist: int, /) -> _Database: ...
|
||||
def CreateRecord(count: int, /) -> _Record: ...
|
||||
|
||||
MSICOLINFO_NAMES: int
|
||||
MSICOLINFO_TYPES: int
|
||||
MSIDBOPEN_CREATE: int
|
||||
MSIDBOPEN_CREATEDIRECT: int
|
||||
MSIDBOPEN_DIRECT: int
|
||||
MSIDBOPEN_PATCHFILE: int
|
||||
MSIDBOPEN_READONLY: int
|
||||
MSIDBOPEN_TRANSACT: int
|
||||
MSIMODIFY_ASSIGN: int
|
||||
MSIMODIFY_DELETE: int
|
||||
MSIMODIFY_INSERT: int
|
||||
MSIMODIFY_INSERT_TEMPORARY: int
|
||||
MSIMODIFY_MERGE: int
|
||||
MSIMODIFY_REFRESH: int
|
||||
MSIMODIFY_REPLACE: int
|
||||
MSIMODIFY_SEEK: int
|
||||
MSIMODIFY_UPDATE: int
|
||||
MSIMODIFY_VALIDATE: int
|
||||
MSIMODIFY_VALIDATE_DELETE: int
|
||||
MSIMODIFY_VALIDATE_FIELD: int
|
||||
MSIMODIFY_VALIDATE_NEW: int
|
||||
|
||||
PID_APPNAME: int
|
||||
PID_AUTHOR: int
|
||||
PID_CHARCOUNT: int
|
||||
PID_CODEPAGE: int
|
||||
PID_COMMENTS: int
|
||||
PID_CREATE_DTM: int
|
||||
PID_KEYWORDS: int
|
||||
PID_LASTAUTHOR: int
|
||||
PID_LASTPRINTED: int
|
||||
PID_LASTSAVE_DTM: int
|
||||
PID_PAGECOUNT: int
|
||||
PID_REVNUMBER: int
|
||||
PID_SECURITY: int
|
||||
PID_SUBJECT: int
|
||||
PID_TEMPLATE: int
|
||||
PID_TITLE: int
|
||||
PID_WORDCOUNT: int
|
||||
@@ -1,147 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import SupportsGetItem
|
||||
from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence
|
||||
from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, SupportsIndex, TypeVar, final, overload
|
||||
from typing_extensions import ParamSpec, TypeAlias, TypeVarTuple, Unpack
|
||||
|
||||
_R = TypeVar("_R")
|
||||
_T = TypeVar("_T")
|
||||
_T_co = TypeVar("_T_co", covariant=True)
|
||||
_T1 = TypeVar("_T1")
|
||||
_T2 = TypeVar("_T2")
|
||||
_K = TypeVar("_K")
|
||||
_V = TypeVar("_V")
|
||||
_P = ParamSpec("_P")
|
||||
_Ts = TypeVarTuple("_Ts")
|
||||
|
||||
# The following protocols return "Any" instead of bool, since the comparison
|
||||
# operators can be overloaded to return an arbitrary object. For example,
|
||||
# the numpy.array comparison dunders return another numpy.array.
|
||||
|
||||
class _SupportsDunderLT(Protocol):
|
||||
def __lt__(self, other: Any, /) -> Any: ...
|
||||
|
||||
class _SupportsDunderGT(Protocol):
|
||||
def __gt__(self, other: Any, /) -> Any: ...
|
||||
|
||||
class _SupportsDunderLE(Protocol):
|
||||
def __le__(self, other: Any, /) -> Any: ...
|
||||
|
||||
class _SupportsDunderGE(Protocol):
|
||||
def __ge__(self, other: Any, /) -> Any: ...
|
||||
|
||||
_SupportsComparison: TypeAlias = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT
|
||||
|
||||
class _SupportsInversion(Protocol[_T_co]):
|
||||
def __invert__(self) -> _T_co: ...
|
||||
|
||||
class _SupportsNeg(Protocol[_T_co]):
|
||||
def __neg__(self) -> _T_co: ...
|
||||
|
||||
class _SupportsPos(Protocol[_T_co]):
|
||||
def __pos__(self) -> _T_co: ...
|
||||
|
||||
# All four comparison functions must have the same signature, or we get false-positive errors
|
||||
def lt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
|
||||
def le(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
|
||||
def eq(a: object, b: object, /) -> Any: ...
|
||||
def ne(a: object, b: object, /) -> Any: ...
|
||||
def ge(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
|
||||
def gt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
|
||||
def not_(a: object, /) -> bool: ...
|
||||
def truth(a: object, /) -> bool: ...
|
||||
def is_(a: object, b: object, /) -> bool: ...
|
||||
def is_not(a: object, b: object, /) -> bool: ...
|
||||
def abs(a: SupportsAbs[_T], /) -> _T: ...
|
||||
def add(a: Any, b: Any, /) -> Any: ...
|
||||
def and_(a: Any, b: Any, /) -> Any: ...
|
||||
def floordiv(a: Any, b: Any, /) -> Any: ...
|
||||
def index(a: SupportsIndex, /) -> int: ...
|
||||
def inv(a: _SupportsInversion[_T_co], /) -> _T_co: ...
|
||||
def invert(a: _SupportsInversion[_T_co], /) -> _T_co: ...
|
||||
def lshift(a: Any, b: Any, /) -> Any: ...
|
||||
def mod(a: Any, b: Any, /) -> Any: ...
|
||||
def mul(a: Any, b: Any, /) -> Any: ...
|
||||
def matmul(a: Any, b: Any, /) -> Any: ...
|
||||
def neg(a: _SupportsNeg[_T_co], /) -> _T_co: ...
|
||||
def or_(a: Any, b: Any, /) -> Any: ...
|
||||
def pos(a: _SupportsPos[_T_co], /) -> _T_co: ...
|
||||
def pow(a: Any, b: Any, /) -> Any: ...
|
||||
def rshift(a: Any, b: Any, /) -> Any: ...
|
||||
def sub(a: Any, b: Any, /) -> Any: ...
|
||||
def truediv(a: Any, b: Any, /) -> Any: ...
|
||||
def xor(a: Any, b: Any, /) -> Any: ...
|
||||
def concat(a: Sequence[_T], b: Sequence[_T], /) -> Sequence[_T]: ...
|
||||
def contains(a: Container[object], b: object, /) -> bool: ...
|
||||
def countOf(a: Iterable[object], b: object, /) -> int: ...
|
||||
@overload
|
||||
def delitem(a: MutableSequence[Any], b: SupportsIndex, /) -> None: ...
|
||||
@overload
|
||||
def delitem(a: MutableSequence[Any], b: slice, /) -> None: ...
|
||||
@overload
|
||||
def delitem(a: MutableMapping[_K, Any], b: _K, /) -> None: ...
|
||||
@overload
|
||||
def getitem(a: Sequence[_T], b: slice, /) -> Sequence[_T]: ...
|
||||
@overload
|
||||
def getitem(a: SupportsGetItem[_K, _V], b: _K, /) -> _V: ...
|
||||
def indexOf(a: Iterable[_T], b: _T, /) -> int: ...
|
||||
@overload
|
||||
def setitem(a: MutableSequence[_T], b: SupportsIndex, c: _T, /) -> None: ...
|
||||
@overload
|
||||
def setitem(a: MutableSequence[_T], b: slice, c: Sequence[_T], /) -> None: ...
|
||||
@overload
|
||||
def setitem(a: MutableMapping[_K, _V], b: _K, c: _V, /) -> None: ...
|
||||
def length_hint(obj: object, default: int = 0, /) -> int: ...
|
||||
@final
|
||||
class attrgetter(Generic[_T_co]):
|
||||
@overload
|
||||
def __new__(cls, attr: str, /) -> attrgetter[Any]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, attr2: str, /) -> attrgetter[tuple[Any, Any]]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, attr2: str, attr3: str, /) -> attrgetter[tuple[Any, Any, Any]]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, attr2: str, attr3: str, attr4: str, /) -> attrgetter[tuple[Any, Any, Any, Any]]: ...
|
||||
@overload
|
||||
def __new__(cls, attr: str, /, *attrs: str) -> attrgetter[tuple[Any, ...]]: ...
|
||||
def __call__(self, obj: Any, /) -> _T_co: ...
|
||||
|
||||
@final
|
||||
class itemgetter(Generic[_T_co]):
|
||||
@overload
|
||||
def __new__(cls, item: _T, /) -> itemgetter[_T]: ...
|
||||
@overload
|
||||
def __new__(cls, item1: _T1, item2: _T2, /, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ...
|
||||
# __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie:
|
||||
# TypeVar "_KT_contra@SupportsGetItem" is contravariant
|
||||
# "tuple[int, int]" is incompatible with protocol "SupportsIndex"
|
||||
# preventing [_T_co, ...] instead of [Any, ...]
|
||||
#
|
||||
# A suspected mypy issue prevents using [..., _T] instead of [..., Any] here.
|
||||
# https://github.com/python/mypy/issues/14032
|
||||
def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ...
|
||||
|
||||
@final
|
||||
class methodcaller:
|
||||
def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ...
|
||||
def __call__(self, obj: Any) -> Any: ...
|
||||
|
||||
def iadd(a: Any, b: Any, /) -> Any: ...
|
||||
def iand(a: Any, b: Any, /) -> Any: ...
|
||||
def iconcat(a: Any, b: Any, /) -> Any: ...
|
||||
def ifloordiv(a: Any, b: Any, /) -> Any: ...
|
||||
def ilshift(a: Any, b: Any, /) -> Any: ...
|
||||
def imod(a: Any, b: Any, /) -> Any: ...
|
||||
def imul(a: Any, b: Any, /) -> Any: ...
|
||||
def imatmul(a: Any, b: Any, /) -> Any: ...
|
||||
def ior(a: Any, b: Any, /) -> Any: ...
|
||||
def ipow(a: Any, b: Any, /) -> Any: ...
|
||||
def irshift(a: Any, b: Any, /) -> Any: ...
|
||||
def isub(a: Any, b: Any, /) -> Any: ...
|
||||
def itruediv(a: Any, b: Any, /) -> Any: ...
|
||||
def ixor(a: Any, b: Any, /) -> Any: ...
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
def call(obj: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...
|
||||
|
||||
def _compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...
|
||||
@@ -1,34 +0,0 @@
|
||||
from collections.abc import Iterable, Sequence
|
||||
from typing import TypeVar
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_K = TypeVar("_K")
|
||||
_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
|
||||
|
||||
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
|
||||
|
||||
def _get_system_version() -> str: ... # undocumented
|
||||
def _remove_original_values(_config_vars: dict[str, str]) -> None: ... # undocumented
|
||||
def _save_modified_value(_config_vars: dict[str, str], cv: str, newvalue: str) -> None: ... # undocumented
|
||||
def _supports_universal_builds() -> bool: ... # undocumented
|
||||
def _find_appropriate_compiler(_config_vars: dict[str, str]) -> dict[str, str]: ... # undocumented
|
||||
def _remove_universal_flags(_config_vars: dict[str, str]) -> dict[str, str]: ... # undocumented
|
||||
def _remove_unsupported_archs(_config_vars: dict[str, str]) -> dict[str, str]: ... # undocumented
|
||||
def _override_all_archs(_config_vars: dict[str, str]) -> dict[str, str]: ... # undocumented
|
||||
def _check_for_unavailable_sdk(_config_vars: dict[str, str]) -> dict[str, str]: ... # undocumented
|
||||
def compiler_fixup(compiler_so: Iterable[str], cc_args: Sequence[str]) -> list[str]: ...
|
||||
def customize_config_vars(_config_vars: dict[str, str]) -> dict[str, str]: ...
|
||||
def customize_compiler(_config_vars: dict[str, str]) -> dict[str, str]: ...
|
||||
def get_platform_osx(
|
||||
_config_vars: dict[str, str], osname: _T, release: _K, machine: _V
|
||||
) -> tuple[str | _T, str | _K, str | _V]: ...
|
||||
@@ -1,33 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import StrOrBytesPath
|
||||
from collections.abc import Callable, Sequence
|
||||
from typing import SupportsIndex
|
||||
|
||||
if sys.platform != "win32":
|
||||
def cloexec_pipe() -> tuple[int, int]: ...
|
||||
def fork_exec(
|
||||
args: Sequence[StrOrBytesPath] | None,
|
||||
executable_list: Sequence[bytes],
|
||||
close_fds: bool,
|
||||
pass_fds: tuple[int, ...],
|
||||
cwd: str,
|
||||
env: Sequence[bytes] | None,
|
||||
p2cread: int,
|
||||
p2cwrite: int,
|
||||
c2pread: int,
|
||||
c2pwrite: int,
|
||||
errread: int,
|
||||
errwrite: int,
|
||||
errpipe_read: int,
|
||||
errpipe_write: int,
|
||||
restore_signals: int,
|
||||
call_setsid: int,
|
||||
pgid_to_set: int,
|
||||
gid: SupportsIndex | None,
|
||||
extra_groups: list[int] | None,
|
||||
uid: SupportsIndex | None,
|
||||
child_umask: int,
|
||||
preexec_fn: Callable[[], None],
|
||||
allow_vfork: bool,
|
||||
/,
|
||||
) -> int: ...
|
||||
@@ -1,14 +0,0 @@
|
||||
import _typeshed
|
||||
from typing import Any, NewType, TypeVar
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
_CacheToken = NewType("_CacheToken", int)
|
||||
|
||||
def get_cache_token() -> _CacheToken: ...
|
||||
|
||||
class ABCMeta(type):
|
||||
def __new__(
|
||||
mcls: type[_typeshed.Self], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], /
|
||||
) -> _typeshed.Self: ...
|
||||
def register(cls, subclass: type[_T]) -> type[_T]: ...
|
||||
@@ -1,43 +0,0 @@
|
||||
# This is a slight lie, the implementations aren't exactly identical
|
||||
# However, in all likelihood, the differences are inconsequential
|
||||
from _decimal import *
|
||||
|
||||
__all__ = [
|
||||
"Decimal",
|
||||
"Context",
|
||||
"DecimalTuple",
|
||||
"DefaultContext",
|
||||
"BasicContext",
|
||||
"ExtendedContext",
|
||||
"DecimalException",
|
||||
"Clamped",
|
||||
"InvalidOperation",
|
||||
"DivisionByZero",
|
||||
"Inexact",
|
||||
"Rounded",
|
||||
"Subnormal",
|
||||
"Overflow",
|
||||
"Underflow",
|
||||
"FloatOperation",
|
||||
"DivisionImpossible",
|
||||
"InvalidContext",
|
||||
"ConversionSyntax",
|
||||
"DivisionUndefined",
|
||||
"ROUND_DOWN",
|
||||
"ROUND_HALF_UP",
|
||||
"ROUND_HALF_EVEN",
|
||||
"ROUND_CEILING",
|
||||
"ROUND_FLOOR",
|
||||
"ROUND_UP",
|
||||
"ROUND_HALF_DOWN",
|
||||
"ROUND_05UP",
|
||||
"setcontext",
|
||||
"getcontext",
|
||||
"localcontext",
|
||||
"MAX_PREC",
|
||||
"MAX_EMAX",
|
||||
"MIN_EMIN",
|
||||
"MIN_ETINY",
|
||||
"HAVE_THREADS",
|
||||
"HAVE_CONTEXTVAR",
|
||||
]
|
||||
@@ -1,12 +0,0 @@
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# Actually Tuple[(int,) * 625]
|
||||
_State: TypeAlias = tuple[int, ...]
|
||||
|
||||
class Random:
|
||||
def __init__(self, seed: object = ...) -> None: ...
|
||||
def seed(self, n: object = None, /) -> None: ...
|
||||
def getstate(self) -> _State: ...
|
||||
def setstate(self, state: _State, /) -> None: ...
|
||||
def random(self) -> float: ...
|
||||
def getrandbits(self, k: int, /) -> int: ...
|
||||
@@ -1,16 +0,0 @@
|
||||
from collections.abc import Iterable
|
||||
from typing import ClassVar, Literal, NoReturn
|
||||
|
||||
class Quitter:
|
||||
name: str
|
||||
eof: str
|
||||
def __init__(self, name: str, eof: str) -> None: ...
|
||||
def __call__(self, code: int | None = None) -> NoReturn: ...
|
||||
|
||||
class _Printer:
|
||||
MAXLINES: ClassVar[Literal[23]]
|
||||
def __init__(self, name: str, data: str, files: Iterable[str] = (), dirs: Iterable[str] = ()) -> None: ...
|
||||
def __call__(self) -> None: ...
|
||||
|
||||
class _Helper:
|
||||
def __call__(self, request: object) -> None: ...
|
||||
@@ -1,803 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer, WriteableBuffer
|
||||
from collections.abc import Iterable
|
||||
from typing import Any, SupportsIndex, overload
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
_CMSG: TypeAlias = tuple[int, int, bytes]
|
||||
_CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer]
|
||||
|
||||
# Addresses can be either tuples of varying lengths (AF_INET, AF_INET6,
|
||||
# AF_NETLINK, AF_TIPC) or strings/buffers (AF_UNIX).
|
||||
# See getsockaddrarg() in socketmodule.c.
|
||||
_Address: TypeAlias = tuple[Any, ...] | str | ReadableBuffer
|
||||
_RetAddress: TypeAlias = Any
|
||||
|
||||
# ===== Constants =====
|
||||
# This matches the order in the CPython documentation
|
||||
# https://docs.python.org/3/library/socket.html#constants
|
||||
|
||||
if sys.platform != "win32":
|
||||
AF_UNIX: int
|
||||
|
||||
AF_INET: int
|
||||
AF_INET6: int
|
||||
|
||||
AF_UNSPEC: int
|
||||
|
||||
SOCK_STREAM: int
|
||||
SOCK_DGRAM: int
|
||||
SOCK_RAW: int
|
||||
SOCK_RDM: int
|
||||
SOCK_SEQPACKET: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.27
|
||||
SOCK_CLOEXEC: int
|
||||
SOCK_NONBLOCK: int
|
||||
|
||||
# --------------------
|
||||
# Many constants of these forms, documented in the Unix documentation on
|
||||
# sockets and/or the IP protocol, are also defined in the socket module.
|
||||
# SO_*
|
||||
# socket.SOMAXCONN
|
||||
# MSG_*
|
||||
# SOL_*
|
||||
# SCM_*
|
||||
# IPPROTO_*
|
||||
# IPPORT_*
|
||||
# INADDR_*
|
||||
# IP_*
|
||||
# IPV6_*
|
||||
# EAI_*
|
||||
# AI_*
|
||||
# NI_*
|
||||
# TCP_*
|
||||
# --------------------
|
||||
|
||||
SO_ACCEPTCONN: int
|
||||
SO_BROADCAST: int
|
||||
SO_DEBUG: int
|
||||
SO_DONTROUTE: int
|
||||
SO_ERROR: int
|
||||
SO_KEEPALIVE: int
|
||||
SO_LINGER: int
|
||||
SO_OOBINLINE: int
|
||||
SO_RCVBUF: int
|
||||
SO_RCVLOWAT: int
|
||||
SO_RCVTIMEO: int
|
||||
SO_REUSEADDR: int
|
||||
SO_SNDBUF: int
|
||||
SO_SNDLOWAT: int
|
||||
SO_SNDTIMEO: int
|
||||
SO_TYPE: int
|
||||
SO_USELOOPBACK: int
|
||||
if sys.platform == "win32":
|
||||
SO_EXCLUSIVEADDRUSE: int
|
||||
if sys.platform != "win32":
|
||||
SO_REUSEPORT: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SO_BINDTODEVICE: int
|
||||
SO_DOMAIN: int
|
||||
SO_MARK: int
|
||||
SO_PASSCRED: int
|
||||
SO_PASSSEC: int
|
||||
SO_PEERCRED: int
|
||||
SO_PEERSEC: int
|
||||
SO_PRIORITY: int
|
||||
SO_PROTOCOL: int
|
||||
SO_SETFIB: int
|
||||
|
||||
SOMAXCONN: int
|
||||
|
||||
MSG_CTRUNC: int
|
||||
MSG_DONTROUTE: int
|
||||
MSG_OOB: int
|
||||
MSG_PEEK: int
|
||||
MSG_TRUNC: int
|
||||
MSG_WAITALL: int
|
||||
if sys.platform != "win32":
|
||||
MSG_DONTWAIT: int
|
||||
MSG_EOF: int
|
||||
MSG_EOR: int
|
||||
MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not
|
||||
if sys.platform != "darwin":
|
||||
MSG_BCAST: int
|
||||
MSG_ERRQUEUE: int
|
||||
MSG_MCAST: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
MSG_BTAG: int
|
||||
MSG_CMSG_CLOEXEC: int
|
||||
MSG_CONFIRM: int
|
||||
MSG_ETAG: int
|
||||
MSG_FASTOPEN: int
|
||||
MSG_MORE: int
|
||||
MSG_NOTIFICATION: int
|
||||
|
||||
SOL_IP: int
|
||||
SOL_SOCKET: int
|
||||
SOL_TCP: int
|
||||
SOL_UDP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SOL_ATALK: int
|
||||
SOL_AX25: int
|
||||
SOL_HCI: int
|
||||
SOL_IPX: int
|
||||
SOL_NETROM: int
|
||||
SOL_ROSE: int
|
||||
|
||||
if sys.platform != "win32":
|
||||
SCM_CREDS: int
|
||||
SCM_RIGHTS: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SCM_CREDENTIALS: int
|
||||
|
||||
IPPROTO_ICMP: int
|
||||
IPPROTO_IP: int
|
||||
IPPROTO_RAW: int
|
||||
IPPROTO_TCP: int
|
||||
IPPROTO_UDP: int
|
||||
IPPROTO_AH: int
|
||||
IPPROTO_DSTOPTS: int
|
||||
IPPROTO_EGP: int
|
||||
IPPROTO_ESP: int
|
||||
IPPROTO_FRAGMENT: int
|
||||
IPPROTO_GGP: int
|
||||
IPPROTO_HOPOPTS: int
|
||||
IPPROTO_ICMPV6: int
|
||||
IPPROTO_IDP: int
|
||||
IPPROTO_IGMP: int
|
||||
IPPROTO_IPV4: int
|
||||
IPPROTO_IPV6: int
|
||||
IPPROTO_MAX: int
|
||||
IPPROTO_ND: int
|
||||
IPPROTO_NONE: int
|
||||
IPPROTO_PIM: int
|
||||
IPPROTO_PUP: int
|
||||
IPPROTO_ROUTING: int
|
||||
IPPROTO_SCTP: int
|
||||
if sys.platform != "darwin":
|
||||
IPPROTO_CBT: int
|
||||
IPPROTO_ICLFXBM: int
|
||||
IPPROTO_IGP: int
|
||||
IPPROTO_L2TP: int
|
||||
IPPROTO_PGM: int
|
||||
IPPROTO_RDP: int
|
||||
IPPROTO_ST: int
|
||||
if sys.platform != "win32":
|
||||
IPPROTO_EON: int
|
||||
IPPROTO_GRE: int
|
||||
IPPROTO_HELLO: int
|
||||
IPPROTO_IPCOMP: int
|
||||
IPPROTO_IPIP: int
|
||||
IPPROTO_RSVP: int
|
||||
IPPROTO_TP: int
|
||||
IPPROTO_XTP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IPPROTO_BIP: int
|
||||
IPPROTO_MOBILE: int
|
||||
IPPROTO_VRRP: int
|
||||
if sys.version_info >= (3, 9) and sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.20, FreeBSD >= 10.1
|
||||
IPPROTO_UDPLITE: int
|
||||
if sys.version_info >= (3, 10) and sys.platform == "linux":
|
||||
IPPROTO_MPTCP: int
|
||||
|
||||
IPPORT_RESERVED: int
|
||||
IPPORT_USERRESERVED: int
|
||||
|
||||
INADDR_ALLHOSTS_GROUP: int
|
||||
INADDR_ANY: int
|
||||
INADDR_BROADCAST: int
|
||||
INADDR_LOOPBACK: int
|
||||
INADDR_MAX_LOCAL_GROUP: int
|
||||
INADDR_NONE: int
|
||||
INADDR_UNSPEC_GROUP: int
|
||||
|
||||
IP_ADD_MEMBERSHIP: int
|
||||
IP_DROP_MEMBERSHIP: int
|
||||
IP_HDRINCL: int
|
||||
IP_MULTICAST_IF: int
|
||||
IP_MULTICAST_LOOP: int
|
||||
IP_MULTICAST_TTL: int
|
||||
IP_OPTIONS: int
|
||||
IP_RECVDSTADDR: int
|
||||
if sys.version_info >= (3, 10):
|
||||
IP_RECVTOS: int
|
||||
elif sys.platform != "win32" and sys.platform != "darwin":
|
||||
IP_RECVTOS: int
|
||||
IP_TOS: int
|
||||
IP_TTL: int
|
||||
if sys.platform != "win32":
|
||||
IP_DEFAULT_MULTICAST_LOOP: int
|
||||
IP_DEFAULT_MULTICAST_TTL: int
|
||||
IP_MAX_MEMBERSHIPS: int
|
||||
IP_RECVOPTS: int
|
||||
IP_RECVRETOPTS: int
|
||||
IP_RETOPTS: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IP_TRANSPARENT: int
|
||||
IP_BIND_ADDRESS_NO_PORT: int
|
||||
if sys.version_info >= (3, 12):
|
||||
IP_ADD_SOURCE_MEMBERSHIP: int
|
||||
IP_BLOCK_SOURCE: int
|
||||
IP_DROP_SOURCE_MEMBERSHIP: int
|
||||
IP_PKTINFO: int
|
||||
IP_UNBLOCK_SOURCE: int
|
||||
|
||||
IPV6_CHECKSUM: int
|
||||
IPV6_JOIN_GROUP: int
|
||||
IPV6_LEAVE_GROUP: int
|
||||
IPV6_MULTICAST_HOPS: int
|
||||
IPV6_MULTICAST_IF: int
|
||||
IPV6_MULTICAST_LOOP: int
|
||||
IPV6_RECVTCLASS: int
|
||||
IPV6_TCLASS: int
|
||||
IPV6_UNICAST_HOPS: int
|
||||
IPV6_V6ONLY: int
|
||||
if sys.version_info >= (3, 9) or sys.platform != "darwin":
|
||||
IPV6_DONTFRAG: int
|
||||
IPV6_HOPLIMIT: int
|
||||
IPV6_HOPOPTS: int
|
||||
IPV6_PKTINFO: int
|
||||
IPV6_RECVRTHDR: int
|
||||
IPV6_RTHDR: int
|
||||
if sys.platform != "win32":
|
||||
IPV6_RTHDR_TYPE_0: int
|
||||
if sys.version_info >= (3, 9) or sys.platform != "darwin":
|
||||
IPV6_DSTOPTS: int
|
||||
IPV6_NEXTHOP: int
|
||||
IPV6_PATHMTU: int
|
||||
IPV6_RECVDSTOPTS: int
|
||||
IPV6_RECVHOPLIMIT: int
|
||||
IPV6_RECVHOPOPTS: int
|
||||
IPV6_RECVPATHMTU: int
|
||||
IPV6_RECVPKTINFO: int
|
||||
IPV6_RTHDRDSTOPTS: int
|
||||
IPV6_USE_MIN_MTU: int
|
||||
|
||||
EAI_AGAIN: int
|
||||
EAI_BADFLAGS: int
|
||||
EAI_FAIL: int
|
||||
EAI_FAMILY: int
|
||||
EAI_MEMORY: int
|
||||
EAI_NODATA: int
|
||||
EAI_NONAME: int
|
||||
EAI_SERVICE: int
|
||||
EAI_SOCKTYPE: int
|
||||
if sys.platform != "win32":
|
||||
EAI_ADDRFAMILY: int
|
||||
EAI_BADHINTS: int
|
||||
EAI_MAX: int
|
||||
EAI_OVERFLOW: int
|
||||
EAI_PROTOCOL: int
|
||||
EAI_SYSTEM: int
|
||||
|
||||
AI_ADDRCONFIG: int
|
||||
AI_ALL: int
|
||||
AI_CANONNAME: int
|
||||
AI_NUMERICHOST: int
|
||||
AI_NUMERICSERV: int
|
||||
AI_PASSIVE: int
|
||||
AI_V4MAPPED: int
|
||||
if sys.platform != "win32":
|
||||
AI_DEFAULT: int
|
||||
AI_MASK: int
|
||||
AI_V4MAPPED_CFG: int
|
||||
|
||||
NI_DGRAM: int
|
||||
NI_MAXHOST: int
|
||||
NI_MAXSERV: int
|
||||
NI_NAMEREQD: int
|
||||
NI_NOFQDN: int
|
||||
NI_NUMERICHOST: int
|
||||
NI_NUMERICSERV: int
|
||||
|
||||
TCP_FASTOPEN: int
|
||||
TCP_KEEPCNT: int
|
||||
TCP_KEEPINTVL: int
|
||||
TCP_MAXSEG: int
|
||||
TCP_NODELAY: int
|
||||
if sys.platform != "win32":
|
||||
TCP_NOTSENT_LOWAT: int
|
||||
if sys.platform != "darwin":
|
||||
TCP_KEEPIDLE: int
|
||||
if sys.version_info >= (3, 10) and sys.platform == "darwin":
|
||||
TCP_KEEPALIVE: int
|
||||
if sys.version_info >= (3, 11) and sys.platform == "darwin":
|
||||
TCP_CONNECTION_INFO: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
TCP_CONGESTION: int
|
||||
TCP_CORK: int
|
||||
TCP_DEFER_ACCEPT: int
|
||||
TCP_INFO: int
|
||||
TCP_LINGER2: int
|
||||
TCP_QUICKACK: int
|
||||
TCP_SYNCNT: int
|
||||
TCP_USER_TIMEOUT: int
|
||||
TCP_WINDOW_CLAMP: int
|
||||
|
||||
# --------------------
|
||||
# Specifically documented constants
|
||||
# --------------------
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.25, NetBSD >= 8
|
||||
AF_CAN: int
|
||||
PF_CAN: int
|
||||
SOL_CAN_BASE: int
|
||||
SOL_CAN_RAW: int
|
||||
CAN_EFF_FLAG: int
|
||||
CAN_EFF_MASK: int
|
||||
CAN_ERR_FLAG: int
|
||||
CAN_ERR_MASK: int
|
||||
CAN_RAW: int
|
||||
CAN_RAW_ERR_FILTER: int
|
||||
CAN_RAW_FILTER: int
|
||||
CAN_RAW_LOOPBACK: int
|
||||
CAN_RAW_RECV_OWN_MSGS: int
|
||||
CAN_RTR_FLAG: int
|
||||
CAN_SFF_MASK: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.25
|
||||
CAN_BCM: int
|
||||
CAN_BCM_TX_SETUP: int
|
||||
CAN_BCM_TX_DELETE: int
|
||||
CAN_BCM_TX_READ: int
|
||||
CAN_BCM_TX_SEND: int
|
||||
CAN_BCM_RX_SETUP: int
|
||||
CAN_BCM_RX_DELETE: int
|
||||
CAN_BCM_RX_READ: int
|
||||
CAN_BCM_TX_STATUS: int
|
||||
CAN_BCM_TX_EXPIRED: int
|
||||
CAN_BCM_RX_STATUS: int
|
||||
CAN_BCM_RX_TIMEOUT: int
|
||||
CAN_BCM_RX_CHANGED: int
|
||||
CAN_BCM_SETTIMER: int
|
||||
CAN_BCM_STARTTIMER: int
|
||||
CAN_BCM_TX_COUNTEVT: int
|
||||
CAN_BCM_TX_ANNOUNCE: int
|
||||
CAN_BCM_TX_CP_CAN_ID: int
|
||||
CAN_BCM_RX_FILTER_ID: int
|
||||
CAN_BCM_RX_CHECK_DLC: int
|
||||
CAN_BCM_RX_NO_AUTOTIMER: int
|
||||
CAN_BCM_RX_ANNOUNCE_RESUME: int
|
||||
CAN_BCM_TX_RESET_MULTI_IDX: int
|
||||
CAN_BCM_RX_RTR_FRAME: int
|
||||
CAN_BCM_CAN_FD_FRAME: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 3.6
|
||||
CAN_RAW_FD_FRAMES: int
|
||||
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 9):
|
||||
# Availability: Linux >= 4.1
|
||||
CAN_RAW_JOIN_FILTERS: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.25
|
||||
CAN_ISOTP: int
|
||||
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 9):
|
||||
# Availability: Linux >= 5.4
|
||||
CAN_J1939: int
|
||||
|
||||
J1939_MAX_UNICAST_ADDR: int
|
||||
J1939_IDLE_ADDR: int
|
||||
J1939_NO_ADDR: int
|
||||
J1939_NO_NAME: int
|
||||
J1939_PGN_REQUEST: int
|
||||
J1939_PGN_ADDRESS_CLAIMED: int
|
||||
J1939_PGN_ADDRESS_COMMANDED: int
|
||||
J1939_PGN_PDU1_MAX: int
|
||||
J1939_PGN_MAX: int
|
||||
J1939_NO_PGN: int
|
||||
|
||||
SO_J1939_FILTER: int
|
||||
SO_J1939_PROMISC: int
|
||||
SO_J1939_SEND_PRIO: int
|
||||
SO_J1939_ERRQUEUE: int
|
||||
|
||||
SCM_J1939_DEST_ADDR: int
|
||||
SCM_J1939_DEST_NAME: int
|
||||
SCM_J1939_PRIO: int
|
||||
SCM_J1939_ERRQUEUE: int
|
||||
|
||||
J1939_NLA_PAD: int
|
||||
J1939_NLA_BYTES_ACKED: int
|
||||
J1939_EE_INFO_NONE: int
|
||||
J1939_EE_INFO_TX_ABORT: int
|
||||
J1939_FILTER_MAX: int
|
||||
|
||||
if sys.version_info >= (3, 12) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin":
|
||||
# Availability: FreeBSD >= 14.0
|
||||
AF_DIVERT: int
|
||||
PF_DIVERT: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.2
|
||||
AF_PACKET: int
|
||||
PF_PACKET: int
|
||||
PACKET_BROADCAST: int
|
||||
PACKET_FASTROUTE: int
|
||||
PACKET_HOST: int
|
||||
PACKET_LOOPBACK: int
|
||||
PACKET_MULTICAST: int
|
||||
PACKET_OTHERHOST: int
|
||||
PACKET_OUTGOING: int
|
||||
|
||||
if sys.version_info >= (3, 12) and sys.platform == "linux":
|
||||
ETH_P_ALL: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.30
|
||||
AF_RDS: int
|
||||
PF_RDS: int
|
||||
SOL_RDS: int
|
||||
RDS_CANCEL_SENT_TO: int
|
||||
RDS_CMSG_RDMA_ARGS: int
|
||||
RDS_CMSG_RDMA_DEST: int
|
||||
RDS_CMSG_RDMA_MAP: int
|
||||
RDS_CMSG_RDMA_STATUS: int
|
||||
RDS_CMSG_RDMA_UPDATE: int
|
||||
RDS_CONG_MONITOR: int
|
||||
RDS_FREE_MR: int
|
||||
RDS_GET_MR: int
|
||||
RDS_GET_MR_FOR_DEST: int
|
||||
RDS_RDMA_DONTWAIT: int
|
||||
RDS_RDMA_FENCE: int
|
||||
RDS_RDMA_INVALIDATE: int
|
||||
RDS_RDMA_NOTIFY_ME: int
|
||||
RDS_RDMA_READWRITE: int
|
||||
RDS_RDMA_SILENT: int
|
||||
RDS_RDMA_USE_ONCE: int
|
||||
RDS_RECVERR: int
|
||||
|
||||
if sys.platform == "win32":
|
||||
SIO_RCVALL: int
|
||||
SIO_KEEPALIVE_VALS: int
|
||||
SIO_LOOPBACK_FAST_PATH: int
|
||||
RCVALL_MAX: int
|
||||
RCVALL_OFF: int
|
||||
RCVALL_ON: int
|
||||
RCVALL_SOCKETLEVELONLY: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
AF_TIPC: int
|
||||
SOL_TIPC: int
|
||||
TIPC_ADDR_ID: int
|
||||
TIPC_ADDR_NAME: int
|
||||
TIPC_ADDR_NAMESEQ: int
|
||||
TIPC_CFG_SRV: int
|
||||
TIPC_CLUSTER_SCOPE: int
|
||||
TIPC_CONN_TIMEOUT: int
|
||||
TIPC_CRITICAL_IMPORTANCE: int
|
||||
TIPC_DEST_DROPPABLE: int
|
||||
TIPC_HIGH_IMPORTANCE: int
|
||||
TIPC_IMPORTANCE: int
|
||||
TIPC_LOW_IMPORTANCE: int
|
||||
TIPC_MEDIUM_IMPORTANCE: int
|
||||
TIPC_NODE_SCOPE: int
|
||||
TIPC_PUBLISHED: int
|
||||
TIPC_SRC_DROPPABLE: int
|
||||
TIPC_SUBSCR_TIMEOUT: int
|
||||
TIPC_SUB_CANCEL: int
|
||||
TIPC_SUB_PORTS: int
|
||||
TIPC_SUB_SERVICE: int
|
||||
TIPC_TOP_SRV: int
|
||||
TIPC_WAIT_FOREVER: int
|
||||
TIPC_WITHDRAWN: int
|
||||
TIPC_ZONE_SCOPE: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.38
|
||||
AF_ALG: int
|
||||
SOL_ALG: int
|
||||
ALG_OP_DECRYPT: int
|
||||
ALG_OP_ENCRYPT: int
|
||||
ALG_OP_SIGN: int
|
||||
ALG_OP_VERIFY: int
|
||||
ALG_SET_AEAD_ASSOCLEN: int
|
||||
ALG_SET_AEAD_AUTHSIZE: int
|
||||
ALG_SET_IV: int
|
||||
ALG_SET_KEY: int
|
||||
ALG_SET_OP: int
|
||||
ALG_SET_PUBKEY: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 4.8 (or maybe 3.9, CPython docs are confusing)
|
||||
AF_VSOCK: int
|
||||
IOCTL_VM_SOCKETS_GET_LOCAL_CID: int
|
||||
VMADDR_CID_ANY: int
|
||||
VMADDR_CID_HOST: int
|
||||
VMADDR_PORT_ANY: int
|
||||
SO_VM_SOCKETS_BUFFER_MAX_SIZE: int
|
||||
SO_VM_SOCKETS_BUFFER_SIZE: int
|
||||
SO_VM_SOCKETS_BUFFER_MIN_SIZE: int
|
||||
VM_SOCKETS_INVALID_VERSION: int # undocumented
|
||||
|
||||
if sys.platform != "win32" or sys.version_info >= (3, 9):
|
||||
# Documented as only available on BSD, macOS, but empirically sometimes
|
||||
# available on Windows
|
||||
AF_LINK: int
|
||||
|
||||
has_ipv6: bool
|
||||
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "win32" or sys.version_info >= (3, 9):
|
||||
BDADDR_ANY: str
|
||||
BDADDR_LOCAL: str
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
HCI_FILTER: int # not in NetBSD or DragonFlyBSD
|
||||
HCI_TIME_STAMP: int # not in FreeBSD, NetBSD, or DragonFlyBSD
|
||||
HCI_DATA_DIR: int # not in FreeBSD, NetBSD, or DragonFlyBSD
|
||||
|
||||
if sys.platform == "linux":
|
||||
AF_QIPCRTR: int # Availability: Linux >= 4.7
|
||||
|
||||
if sys.version_info >= (3, 11) and sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin":
|
||||
# FreeBSD
|
||||
SCM_CREDS2: int
|
||||
LOCAL_CREDS: int
|
||||
LOCAL_CREDS_PERSISTENT: int
|
||||
|
||||
if sys.version_info >= (3, 11) and sys.platform == "linux":
|
||||
SO_INCOMING_CPU: int # Availability: Linux >= 3.9
|
||||
|
||||
if sys.version_info >= (3, 12) and sys.platform == "win32":
|
||||
# Availability: Windows
|
||||
AF_HYPERV: int
|
||||
HV_PROTOCOL_RAW: int
|
||||
HVSOCKET_CONNECT_TIMEOUT: int
|
||||
HVSOCKET_CONNECT_TIMEOUT_MAX: int
|
||||
HVSOCKET_CONNECTED_SUSPEND: int
|
||||
HVSOCKET_ADDRESS_FLAG_PASSTHRU: int
|
||||
HV_GUID_ZERO: str
|
||||
HV_GUID_WILDCARD: str
|
||||
HV_GUID_BROADCAST: str
|
||||
HV_GUID_CHILDREN: str
|
||||
HV_GUID_LOOPBACK: str
|
||||
HV_GUID_PARENT: str
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
if sys.platform != "win32":
|
||||
# Availability: Linux, FreeBSD, macOS
|
||||
ETHERTYPE_ARP: int
|
||||
ETHERTYPE_IP: int
|
||||
ETHERTYPE_IPV6: int
|
||||
ETHERTYPE_VLAN: int
|
||||
|
||||
# --------------------
|
||||
# Semi-documented constants
|
||||
# These are alluded to under the "Socket families" section in the docs
|
||||
# https://docs.python.org/3/library/socket.html#socket-families
|
||||
# --------------------
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Netlink is defined by Linux
|
||||
AF_NETLINK: int
|
||||
NETLINK_ARPD: int
|
||||
NETLINK_CRYPTO: int
|
||||
NETLINK_DNRTMSG: int
|
||||
NETLINK_FIREWALL: int
|
||||
NETLINK_IP6_FW: int
|
||||
NETLINK_NFLOG: int
|
||||
NETLINK_ROUTE6: int
|
||||
NETLINK_ROUTE: int
|
||||
NETLINK_SKIP: int
|
||||
NETLINK_TAPBASE: int
|
||||
NETLINK_TCPDIAG: int
|
||||
NETLINK_USERSOCK: int
|
||||
NETLINK_W1: int
|
||||
NETLINK_XFRM: int
|
||||
|
||||
if sys.platform == "darwin":
|
||||
PF_SYSTEM: int
|
||||
SYSPROTO_CONTROL: int
|
||||
|
||||
if sys.platform != "darwin":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "win32":
|
||||
AF_BLUETOOTH: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
# Linux and some BSD support is explicit in the docs
|
||||
# Windows and macOS do not support in practice
|
||||
BTPROTO_HCI: int
|
||||
BTPROTO_L2CAP: int
|
||||
BTPROTO_SCO: int # not in FreeBSD
|
||||
if sys.platform != "darwin":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "win32":
|
||||
BTPROTO_RFCOMM: int
|
||||
|
||||
if sys.version_info >= (3, 9) and sys.platform == "linux":
|
||||
UDPLITE_RECV_CSCOV: int
|
||||
UDPLITE_SEND_CSCOV: int
|
||||
|
||||
# --------------------
|
||||
# Documented under socket.shutdown
|
||||
# --------------------
|
||||
SHUT_RD: int
|
||||
SHUT_RDWR: int
|
||||
SHUT_WR: int
|
||||
|
||||
# --------------------
|
||||
# Undocumented constants
|
||||
# --------------------
|
||||
|
||||
# Undocumented address families
|
||||
AF_APPLETALK: int
|
||||
AF_DECnet: int
|
||||
AF_IPX: int
|
||||
AF_SNA: int
|
||||
|
||||
if sys.platform != "win32":
|
||||
AF_ROUTE: int
|
||||
AF_SYSTEM: int
|
||||
|
||||
if sys.platform != "darwin":
|
||||
AF_IRDA: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
AF_AAL5: int
|
||||
AF_ASH: int
|
||||
AF_ATMPVC: int
|
||||
AF_ATMSVC: int
|
||||
AF_AX25: int
|
||||
AF_BRIDGE: int
|
||||
AF_ECONET: int
|
||||
AF_KEY: int
|
||||
AF_LLC: int
|
||||
AF_NETBEUI: int
|
||||
AF_NETROM: int
|
||||
AF_PPPOX: int
|
||||
AF_ROSE: int
|
||||
AF_SECURITY: int
|
||||
AF_WANPIPE: int
|
||||
AF_X25: int
|
||||
|
||||
# Miscellaneous undocumented
|
||||
|
||||
if sys.platform != "win32":
|
||||
LOCAL_PEERCRED: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IPX_TYPE: int
|
||||
|
||||
# ===== Exceptions =====
|
||||
|
||||
error = OSError
|
||||
|
||||
class herror(error): ...
|
||||
class gaierror(error): ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
timeout = TimeoutError
|
||||
else:
|
||||
class timeout(error): ...
|
||||
|
||||
# ===== Classes =====
|
||||
|
||||
class socket:
|
||||
@property
|
||||
def family(self) -> int: ...
|
||||
@property
|
||||
def type(self) -> int: ...
|
||||
@property
|
||||
def proto(self) -> int: ...
|
||||
@property
|
||||
def timeout(self) -> float | None: ...
|
||||
if sys.platform == "win32":
|
||||
def __init__(
|
||||
self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = ...
|
||||
) -> None: ...
|
||||
else:
|
||||
def __init__(self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | None = ...) -> None: ...
|
||||
|
||||
def bind(self, address: _Address, /) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def connect(self, address: _Address, /) -> None: ...
|
||||
def connect_ex(self, address: _Address, /) -> int: ...
|
||||
def detach(self) -> int: ...
|
||||
def fileno(self) -> int: ...
|
||||
def getpeername(self) -> _RetAddress: ...
|
||||
def getsockname(self) -> _RetAddress: ...
|
||||
@overload
|
||||
def getsockopt(self, level: int, optname: int, /) -> int: ...
|
||||
@overload
|
||||
def getsockopt(self, level: int, optname: int, buflen: int, /) -> bytes: ...
|
||||
def getblocking(self) -> bool: ...
|
||||
def gettimeout(self) -> float | None: ...
|
||||
if sys.platform == "win32":
|
||||
def ioctl(self, control: int, option: int | tuple[int, int, int] | bool, /) -> None: ...
|
||||
|
||||
def listen(self, backlog: int = ..., /) -> None: ...
|
||||
def recv(self, bufsize: int, flags: int = ..., /) -> bytes: ...
|
||||
def recvfrom(self, bufsize: int, flags: int = ..., /) -> tuple[bytes, _RetAddress]: ...
|
||||
if sys.platform != "win32":
|
||||
def recvmsg(self, bufsize: int, ancbufsize: int = ..., flags: int = ..., /) -> tuple[bytes, list[_CMSG], int, Any]: ...
|
||||
def recvmsg_into(
|
||||
self, buffers: Iterable[WriteableBuffer], ancbufsize: int = ..., flags: int = ..., /
|
||||
) -> tuple[int, list[_CMSG], int, Any]: ...
|
||||
|
||||
def recvfrom_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> tuple[int, _RetAddress]: ...
|
||||
def recv_into(self, buffer: WriteableBuffer, nbytes: int = ..., flags: int = ...) -> int: ...
|
||||
def send(self, data: ReadableBuffer, flags: int = ..., /) -> int: ...
|
||||
def sendall(self, data: ReadableBuffer, flags: int = ..., /) -> None: ...
|
||||
@overload
|
||||
def sendto(self, data: ReadableBuffer, address: _Address, /) -> int: ...
|
||||
@overload
|
||||
def sendto(self, data: ReadableBuffer, flags: int, address: _Address, /) -> int: ...
|
||||
if sys.platform != "win32":
|
||||
def sendmsg(
|
||||
self,
|
||||
buffers: Iterable[ReadableBuffer],
|
||||
ancdata: Iterable[_CMSGArg] = ...,
|
||||
flags: int = ...,
|
||||
address: _Address | None = ...,
|
||||
/,
|
||||
) -> int: ...
|
||||
if sys.platform == "linux":
|
||||
def sendmsg_afalg(
|
||||
self, msg: Iterable[ReadableBuffer] = ..., *, op: int, iv: Any = ..., assoclen: int = ..., flags: int = ...
|
||||
) -> int: ...
|
||||
|
||||
def setblocking(self, flag: bool, /) -> None: ...
|
||||
def settimeout(self, value: float | None, /) -> None: ...
|
||||
@overload
|
||||
def setsockopt(self, level: int, optname: int, value: int | ReadableBuffer, /) -> None: ...
|
||||
@overload
|
||||
def setsockopt(self, level: int, optname: int, value: None, optlen: int, /) -> None: ...
|
||||
if sys.platform == "win32":
|
||||
def share(self, process_id: int, /) -> bytes: ...
|
||||
|
||||
def shutdown(self, how: int, /) -> None: ...
|
||||
|
||||
SocketType = socket
|
||||
|
||||
# ===== Functions =====
|
||||
|
||||
def close(fd: SupportsIndex, /) -> None: ...
|
||||
def dup(fd: SupportsIndex, /) -> int: ...
|
||||
|
||||
# the 5th tuple item is an address
|
||||
def getaddrinfo(
|
||||
host: bytes | str | None,
|
||||
port: bytes | str | int | None,
|
||||
family: int = ...,
|
||||
type: int = ...,
|
||||
proto: int = ...,
|
||||
flags: int = ...,
|
||||
) -> list[tuple[int, int, int, str, tuple[str, int] | tuple[str, int, int, int]]]: ...
|
||||
def gethostbyname(hostname: str, /) -> str: ...
|
||||
def gethostbyname_ex(hostname: str, /) -> tuple[str, list[str], list[str]]: ...
|
||||
def gethostname() -> str: ...
|
||||
def gethostbyaddr(ip_address: str, /) -> tuple[str, list[str], list[str]]: ...
|
||||
def getnameinfo(sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int, /) -> tuple[str, str]: ...
|
||||
def getprotobyname(protocolname: str, /) -> int: ...
|
||||
def getservbyname(servicename: str, protocolname: str = ..., /) -> int: ...
|
||||
def getservbyport(port: int, protocolname: str = ..., /) -> str: ...
|
||||
def ntohl(x: int, /) -> int: ... # param & ret val are 32-bit ints
|
||||
def ntohs(x: int, /) -> int: ... # param & ret val are 16-bit ints
|
||||
def htonl(x: int, /) -> int: ... # param & ret val are 32-bit ints
|
||||
def htons(x: int, /) -> int: ... # param & ret val are 16-bit ints
|
||||
def inet_aton(ip_addr: str, /) -> bytes: ... # ret val 4 bytes in length
|
||||
def inet_ntoa(packed_ip: ReadableBuffer, /) -> str: ...
|
||||
def inet_pton(address_family: int, ip_string: str, /) -> bytes: ...
|
||||
def inet_ntop(address_family: int, packed_ip: ReadableBuffer, /) -> str: ...
|
||||
def getdefaulttimeout() -> float | None: ...
|
||||
def setdefaulttimeout(timeout: float | None, /) -> None: ...
|
||||
|
||||
if sys.platform != "win32":
|
||||
def sethostname(name: str, /) -> None: ...
|
||||
def CMSG_LEN(length: int, /) -> int: ...
|
||||
def CMSG_SPACE(length: int, /) -> int: ...
|
||||
def socketpair(family: int = ..., type: int = ..., proto: int = ..., /) -> tuple[socket, socket]: ...
|
||||
|
||||
def if_nameindex() -> list[tuple[int, str]]: ...
|
||||
def if_nametoindex(oname: str, /) -> int: ...
|
||||
def if_indextoname(index: int, /) -> str: ...
|
||||
|
||||
CAPI: object
|
||||
@@ -1,117 +0,0 @@
|
||||
import sys
|
||||
from typing import Literal
|
||||
|
||||
SF_APPEND: Literal[0x00040000]
|
||||
SF_ARCHIVED: Literal[0x00010000]
|
||||
SF_IMMUTABLE: Literal[0x00020000]
|
||||
SF_NOUNLINK: Literal[0x00100000]
|
||||
SF_SNAPSHOT: Literal[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]
|
||||
|
||||
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]
|
||||
|
||||
# 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.
|
||||
S_IFDOOR: int
|
||||
S_IFPORT: int
|
||||
S_IFWHT: int
|
||||
|
||||
S_ISUID: Literal[0o4000]
|
||||
S_ISGID: Literal[0o2000]
|
||||
S_ISVTX: Literal[0o1000]
|
||||
|
||||
S_IRWXU: Literal[0o0700]
|
||||
S_IRUSR: Literal[0o0400]
|
||||
S_IWUSR: Literal[0o0200]
|
||||
S_IXUSR: Literal[0o0100]
|
||||
|
||||
S_IRWXG: Literal[0o0070]
|
||||
S_IRGRP: Literal[0o0040]
|
||||
S_IWGRP: Literal[0o0020]
|
||||
S_IXGRP: Literal[0o0010]
|
||||
|
||||
S_IRWXO: Literal[0o0007]
|
||||
S_IROTH: Literal[0o0004]
|
||||
S_IWOTH: Literal[0o0002]
|
||||
S_IXOTH: Literal[0o0001]
|
||||
|
||||
S_ENFMT: Literal[0o2000]
|
||||
S_IREAD: Literal[0o0400]
|
||||
S_IWRITE: Literal[0o0200]
|
||||
S_IEXEC: Literal[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]
|
||||
|
||||
def S_IMODE(mode: int, /) -> int: ...
|
||||
def S_IFMT(mode: int, /) -> int: ...
|
||||
def S_ISBLK(mode: int, /) -> bool: ...
|
||||
def S_ISCHR(mode: int, /) -> bool: ...
|
||||
def S_ISDIR(mode: int, /) -> bool: ...
|
||||
def S_ISDOOR(mode: int, /) -> bool: ...
|
||||
def S_ISFIFO(mode: int, /) -> bool: ...
|
||||
def S_ISLNK(mode: int, /) -> bool: ...
|
||||
def S_ISPORT(mode: int, /) -> bool: ...
|
||||
def S_ISREG(mode: int, /) -> bool: ...
|
||||
def S_ISSOCK(mode: int, /) -> bool: ...
|
||||
def S_ISWHT(mode: int, /) -> bool: ...
|
||||
def filemode(mode: int, /) -> str: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
IO_REPARSE_TAG_SYMLINK: int
|
||||
IO_REPARSE_TAG_MOUNT_POINT: int
|
||||
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]
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
SF_SETTABLE: Literal[0x3FFF0000]
|
||||
# https://github.com/python/cpython/issues/114081#issuecomment-2119017790
|
||||
# SF_RESTRICTED: Literal[0x00080000]
|
||||
SF_FIRMLINK: Literal[0x00800000]
|
||||
SF_DATALESS: Literal[0x40000000]
|
||||
|
||||
SF_SUPPORTED: Literal[0x9F0000]
|
||||
SF_SYNTHETIC: Literal[0xC0000000]
|
||||
|
||||
UF_TRACKED: Literal[0x00000040]
|
||||
UF_DATAVAULT: Literal[0x00000080]
|
||||
UF_SETTABLE: Literal[0x0000FFFF]
|
||||
@@ -1,59 +0,0 @@
|
||||
import sys
|
||||
from _typeshed import structseq
|
||||
from collections.abc import Callable
|
||||
from threading import Thread
|
||||
from types import TracebackType
|
||||
from typing import Any, Final, NoReturn, final, overload
|
||||
from typing_extensions import TypeVarTuple, Unpack
|
||||
|
||||
_Ts = TypeVarTuple("_Ts")
|
||||
|
||||
error = RuntimeError
|
||||
|
||||
def _count() -> int: ...
|
||||
@final
|
||||
class LockType:
|
||||
def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ...
|
||||
def release(self) -> None: ...
|
||||
def locked(self) -> bool: ...
|
||||
def __enter__(self) -> bool: ...
|
||||
def __exit__(
|
||||
self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> int: ...
|
||||
@overload
|
||||
def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> int: ...
|
||||
def interrupt_main() -> None: ...
|
||||
def exit() -> NoReturn: ...
|
||||
def allocate_lock() -> LockType: ...
|
||||
def get_ident() -> int: ...
|
||||
def stack_size(size: int = ...) -> int: ...
|
||||
|
||||
TIMEOUT_MAX: float
|
||||
|
||||
def get_native_id() -> int: ... # only available on some platforms
|
||||
@final
|
||||
class _ExceptHookArgs(structseq[Any], tuple[type[BaseException], BaseException | None, TracebackType | None, Thread | None]):
|
||||
if sys.version_info >= (3, 10):
|
||||
__match_args__: Final = ("exc_type", "exc_value", "exc_traceback", "thread")
|
||||
|
||||
@property
|
||||
def exc_type(self) -> type[BaseException]: ...
|
||||
@property
|
||||
def exc_value(self) -> BaseException | None: ...
|
||||
@property
|
||||
def exc_traceback(self) -> TracebackType | None: ...
|
||||
@property
|
||||
def thread(self) -> Thread | None: ...
|
||||
|
||||
_excepthook: Callable[[_ExceptHookArgs], Any]
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
def daemon_threads_allowed() -> bool: ...
|
||||
|
||||
class _local:
|
||||
def __getattribute__(self, name: str, /) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any, /) -> None: ...
|
||||
def __delattr__(self, name: str, /) -> None: ...
|
||||
@@ -1,17 +0,0 @@
|
||||
from typing import Any
|
||||
from typing_extensions import TypeAlias
|
||||
from weakref import ReferenceType
|
||||
|
||||
__all__ = ["local"]
|
||||
_LocalDict: TypeAlias = dict[Any, Any]
|
||||
|
||||
class _localimpl:
|
||||
key: str
|
||||
dicts: dict[int, tuple[ReferenceType[Any], _LocalDict]]
|
||||
def get_dict(self) -> _LocalDict: ...
|
||||
def create_dict(self) -> _LocalDict: ...
|
||||
|
||||
class local:
|
||||
def __getattribute__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
def __delattr__(self, name: str) -> None: ...
|
||||
@@ -1,128 +0,0 @@
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from typing import Any, ClassVar, Literal, final
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# _tkinter is meant to be only used internally by tkinter, but some tkinter
|
||||
# functions e.g. return _tkinter.Tcl_Obj objects. Tcl_Obj represents a Tcl
|
||||
# object that hasn't been converted to a string.
|
||||
#
|
||||
# There are not many ways to get Tcl_Objs from tkinter, and I'm not sure if the
|
||||
# only existing ways are supposed to return Tcl_Objs as opposed to returning
|
||||
# strings. Here's one of these things that return Tcl_Objs:
|
||||
#
|
||||
# >>> import tkinter
|
||||
# >>> text = tkinter.Text()
|
||||
# >>> text.tag_add('foo', '1.0', 'end')
|
||||
# >>> text.tag_ranges('foo')
|
||||
# (<textindex object: '1.0'>, <textindex object: '2.0'>)
|
||||
@final
|
||||
class Tcl_Obj:
|
||||
@property
|
||||
def string(self) -> str: ...
|
||||
@property
|
||||
def typename(self) -> str: ...
|
||||
__hash__: ClassVar[None] # type: ignore[assignment]
|
||||
def __eq__(self, value, /): ...
|
||||
def __ge__(self, value, /): ...
|
||||
def __gt__(self, value, /): ...
|
||||
def __le__(self, value, /): ...
|
||||
def __lt__(self, value, /): ...
|
||||
def __ne__(self, value, /): ...
|
||||
|
||||
class TclError(Exception): ...
|
||||
|
||||
_TkinterTraceFunc: TypeAlias = Callable[[tuple[str, ...]], object]
|
||||
|
||||
# This class allows running Tcl code. Tkinter uses it internally a lot, and
|
||||
# it's often handy to drop a piece of Tcl code into a tkinter program. Example:
|
||||
#
|
||||
# >>> import tkinter, _tkinter
|
||||
# >>> tkapp = tkinter.Tk().tk
|
||||
# >>> isinstance(tkapp, _tkinter.TkappType)
|
||||
# True
|
||||
# >>> tkapp.call('set', 'foo', (1,2,3))
|
||||
# (1, 2, 3)
|
||||
# >>> tkapp.eval('return $foo')
|
||||
# '1 2 3'
|
||||
# >>>
|
||||
#
|
||||
# call args can be pretty much anything. Also, call(some_tuple) is same as call(*some_tuple).
|
||||
#
|
||||
# eval always returns str because _tkinter_tkapp_eval_impl in _tkinter.c calls
|
||||
# Tkapp_UnicodeResult, and it returns a string when it succeeds.
|
||||
@final
|
||||
class TkappType:
|
||||
# Please keep in sync with tkinter.Tk
|
||||
def adderrorinfo(self, msg, /): ...
|
||||
def call(self, command: Any, /, *args: Any) -> Any: ...
|
||||
def createcommand(self, name, func, /): ...
|
||||
if sys.platform != "win32":
|
||||
def createfilehandler(self, file, mask, func, /): ...
|
||||
def deletefilehandler(self, file, /): ...
|
||||
|
||||
def createtimerhandler(self, milliseconds, func, /): ...
|
||||
def deletecommand(self, name, /): ...
|
||||
def dooneevent(self, flags: int = 0, /): ...
|
||||
def eval(self, script: str, /) -> str: ...
|
||||
def evalfile(self, fileName, /): ...
|
||||
def exprboolean(self, s, /): ...
|
||||
def exprdouble(self, s, /): ...
|
||||
def exprlong(self, s, /): ...
|
||||
def exprstring(self, s, /): ...
|
||||
def getboolean(self, arg, /): ...
|
||||
def getdouble(self, arg, /): ...
|
||||
def getint(self, arg, /): ...
|
||||
def getvar(self, *args, **kwargs): ...
|
||||
def globalgetvar(self, *args, **kwargs): ...
|
||||
def globalsetvar(self, *args, **kwargs): ...
|
||||
def globalunsetvar(self, *args, **kwargs): ...
|
||||
def interpaddr(self): ...
|
||||
def loadtk(self) -> None: ...
|
||||
def mainloop(self, threshold: int = 0, /): ...
|
||||
def quit(self): ...
|
||||
def record(self, script, /): ...
|
||||
def setvar(self, *ags, **kwargs): ...
|
||||
if sys.version_info < (3, 11):
|
||||
def split(self, arg, /): ...
|
||||
|
||||
def splitlist(self, arg, /): ...
|
||||
def unsetvar(self, *args, **kwargs): ...
|
||||
def wantobjects(self, *args, **kwargs): ...
|
||||
def willdispatch(self): ...
|
||||
if sys.version_info >= (3, 12):
|
||||
def gettrace(self, /) -> _TkinterTraceFunc | None: ...
|
||||
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]
|
||||
|
||||
DONT_WAIT: Literal[2]
|
||||
EXCEPTION: Literal[8]
|
||||
READABLE: Literal[2]
|
||||
WRITABLE: Literal[4]
|
||||
|
||||
TCL_VERSION: str
|
||||
TK_VERSION: str
|
||||
|
||||
@final
|
||||
class TkttType:
|
||||
def deletetimerhandler(self): ...
|
||||
|
||||
def create(
|
||||
screenName: str | None = None,
|
||||
baseName: str = "",
|
||||
className: str = "Tk",
|
||||
interactive: bool = False,
|
||||
wantobjects: bool = False,
|
||||
wantTk: bool = True,
|
||||
sync: bool = False,
|
||||
use: str | None = None,
|
||||
/,
|
||||
): ...
|
||||
def getbusywaitinterval(): ...
|
||||
def setbusywaitinterval(new_val, /): ...
|
||||
@@ -1,17 +0,0 @@
|
||||
import sys
|
||||
from collections.abc import Sequence
|
||||
from tracemalloc import _FrameTuple, _TraceTuple
|
||||
|
||||
def _get_object_traceback(obj: object, /) -> Sequence[_FrameTuple] | None: ...
|
||||
def _get_traces() -> Sequence[_TraceTuple]: ...
|
||||
def clear_traces() -> None: ...
|
||||
def get_traceback_limit() -> int: ...
|
||||
def get_traced_memory() -> tuple[int, int]: ...
|
||||
def get_tracemalloc_memory() -> int: ...
|
||||
def is_tracing() -> bool: ...
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
def reset_peak() -> None: ...
|
||||
|
||||
def start(nframe: int = 1, /) -> None: ...
|
||||
def stop() -> None: ...
|
||||
@@ -1,34 +0,0 @@
|
||||
# Utility types for typeshed
|
||||
|
||||
This package and its submodules contains various common types used by
|
||||
typeshed. It can also be used by packages outside typeshed, but beware
|
||||
the API stability guarantees below.
|
||||
|
||||
## Usage
|
||||
|
||||
The `_typeshed` package and its types do not exist at runtime, but can be
|
||||
used freely in stubs (`.pyi`) files. To import the types from this package in
|
||||
implementation (`.py`) files, use the following construct:
|
||||
|
||||
```python
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import ...
|
||||
```
|
||||
|
||||
Types can then be used in annotations by either quoting them or
|
||||
using:
|
||||
|
||||
```python
|
||||
from __future__ import annotations
|
||||
```
|
||||
|
||||
## API Stability
|
||||
|
||||
You can use this package and its submodules outside of typeshed, but we
|
||||
guarantee only limited API stability. Items marked as "stable" will not be
|
||||
removed or changed in an incompatible way for at least one year.
|
||||
Before making such a change, the "stable" moniker will be removed
|
||||
and we will mark the type in question as deprecated. No guarantees
|
||||
are made about unmarked types.
|
||||
@@ -1,363 +0,0 @@
|
||||
# Utility types for typeshed
|
||||
#
|
||||
# See the README.md file in this directory for more information.
|
||||
|
||||
import sys
|
||||
from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as AbstractSet, Sized
|
||||
from dataclasses import Field
|
||||
from os import PathLike
|
||||
from types import FrameType, TracebackType
|
||||
from typing import (
|
||||
Any,
|
||||
AnyStr,
|
||||
ClassVar,
|
||||
Final,
|
||||
Generic,
|
||||
Literal,
|
||||
Protocol,
|
||||
SupportsFloat,
|
||||
SupportsIndex,
|
||||
SupportsInt,
|
||||
TypeVar,
|
||||
final,
|
||||
overload,
|
||||
)
|
||||
from typing_extensions import Buffer, LiteralString, TypeAlias
|
||||
|
||||
_KT = TypeVar("_KT")
|
||||
_KT_co = TypeVar("_KT_co", covariant=True)
|
||||
_KT_contra = TypeVar("_KT_contra", contravariant=True)
|
||||
_VT = TypeVar("_VT")
|
||||
_VT_co = TypeVar("_VT_co", covariant=True)
|
||||
_T = TypeVar("_T")
|
||||
_T_co = TypeVar("_T_co", covariant=True)
|
||||
_T_contra = TypeVar("_T_contra", contravariant=True)
|
||||
|
||||
# Alternative to `typing_extensions.Self`, exclusively for use with `__new__`
|
||||
# in metaclasses:
|
||||
# def __new__(cls: type[Self], ...) -> Self: ...
|
||||
# In other cases, use `typing_extensions.Self`.
|
||||
Self = TypeVar("Self") # noqa: Y001
|
||||
|
||||
# covariant version of typing.AnyStr, useful for protocols
|
||||
AnyStr_co = TypeVar("AnyStr_co", str, bytes, covariant=True) # noqa: Y001
|
||||
|
||||
# For partially known annotations. Usually, fields where type annotations
|
||||
# haven't been added are left unannotated, but in some situations this
|
||||
# isn't possible or a type is already partially known. In cases like these,
|
||||
# use Incomplete instead of Any as a marker. For example, use
|
||||
# "Incomplete | None" instead of "Any | None".
|
||||
Incomplete: TypeAlias = Any # stable
|
||||
|
||||
# To describe a function parameter that is unused and will work with anything.
|
||||
Unused: TypeAlias = object # stable
|
||||
|
||||
# Marker for return types that include None, but where forcing the user to
|
||||
# check for None can be detrimental. Sometimes called "the Any trick". See
|
||||
# CONTRIBUTING.md for more information.
|
||||
MaybeNone: TypeAlias = Any # stable
|
||||
|
||||
# Used to mark arguments that default to a sentinel value. This prevents
|
||||
# stubtest from complaining about the default value not matching.
|
||||
#
|
||||
# def foo(x: int | None = sentinel) -> None: ...
|
||||
#
|
||||
# In cases where the sentinel object is exported and can be used by user code,
|
||||
# a construct like this is better:
|
||||
#
|
||||
# _SentinelType = NewType("_SentinelType", object)
|
||||
# sentinel: _SentinelType
|
||||
# def foo(x: int | None | _SentinelType = ...) -> None: ...
|
||||
sentinel: Any
|
||||
|
||||
# stable
|
||||
class IdentityFunction(Protocol):
|
||||
def __call__(self, x: _T, /) -> _T: ...
|
||||
|
||||
# stable
|
||||
class SupportsNext(Protocol[_T_co]):
|
||||
def __next__(self) -> _T_co: ...
|
||||
|
||||
# stable
|
||||
class SupportsAnext(Protocol[_T_co]):
|
||||
def __anext__(self) -> Awaitable[_T_co]: ...
|
||||
|
||||
# Comparison protocols
|
||||
|
||||
class SupportsDunderLT(Protocol[_T_contra]):
|
||||
def __lt__(self, other: _T_contra, /) -> bool: ...
|
||||
|
||||
class SupportsDunderGT(Protocol[_T_contra]):
|
||||
def __gt__(self, other: _T_contra, /) -> bool: ...
|
||||
|
||||
class SupportsDunderLE(Protocol[_T_contra]):
|
||||
def __le__(self, other: _T_contra, /) -> bool: ...
|
||||
|
||||
class SupportsDunderGE(Protocol[_T_contra]):
|
||||
def __ge__(self, other: _T_contra, /) -> bool: ...
|
||||
|
||||
class SupportsAllComparisons(
|
||||
SupportsDunderLT[Any], SupportsDunderGT[Any], SupportsDunderLE[Any], SupportsDunderGE[Any], Protocol
|
||||
): ...
|
||||
|
||||
SupportsRichComparison: TypeAlias = SupportsDunderLT[Any] | SupportsDunderGT[Any]
|
||||
SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison) # noqa: Y001
|
||||
|
||||
# Dunder protocols
|
||||
|
||||
class SupportsAdd(Protocol[_T_contra, _T_co]):
|
||||
def __add__(self, x: _T_contra, /) -> _T_co: ...
|
||||
|
||||
class SupportsRAdd(Protocol[_T_contra, _T_co]):
|
||||
def __radd__(self, x: _T_contra, /) -> _T_co: ...
|
||||
|
||||
class SupportsSub(Protocol[_T_contra, _T_co]):
|
||||
def __sub__(self, x: _T_contra, /) -> _T_co: ...
|
||||
|
||||
class SupportsRSub(Protocol[_T_contra, _T_co]):
|
||||
def __rsub__(self, x: _T_contra, /) -> _T_co: ...
|
||||
|
||||
class SupportsDivMod(Protocol[_T_contra, _T_co]):
|
||||
def __divmod__(self, other: _T_contra, /) -> _T_co: ...
|
||||
|
||||
class SupportsRDivMod(Protocol[_T_contra, _T_co]):
|
||||
def __rdivmod__(self, other: _T_contra, /) -> _T_co: ...
|
||||
|
||||
# This protocol is generic over the iterator type, while Iterable is
|
||||
# generic over the type that is iterated over.
|
||||
class SupportsIter(Protocol[_T_co]):
|
||||
def __iter__(self) -> _T_co: ...
|
||||
|
||||
# This protocol is generic over the iterator type, while AsyncIterable is
|
||||
# generic over the type that is iterated over.
|
||||
class SupportsAiter(Protocol[_T_co]):
|
||||
def __aiter__(self) -> _T_co: ...
|
||||
|
||||
class SupportsLenAndGetItem(Protocol[_T_co]):
|
||||
def __len__(self) -> int: ...
|
||||
def __getitem__(self, k: int, /) -> _T_co: ...
|
||||
|
||||
class SupportsTrunc(Protocol):
|
||||
def __trunc__(self) -> int: ...
|
||||
|
||||
# Mapping-like protocols
|
||||
|
||||
# stable
|
||||
class SupportsItems(Protocol[_KT_co, _VT_co]):
|
||||
def items(self) -> AbstractSet[tuple[_KT_co, _VT_co]]: ...
|
||||
|
||||
# stable
|
||||
class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]):
|
||||
def keys(self) -> Iterable[_KT]: ...
|
||||
def __getitem__(self, key: _KT, /) -> _VT_co: ...
|
||||
|
||||
# This protocol is currently under discussion. Use SupportsContainsAndGetItem
|
||||
# instead, if you require the __contains__ method.
|
||||
# See https://github.com/python/typeshed/issues/11822.
|
||||
class SupportsGetItem(Protocol[_KT_contra, _VT_co]):
|
||||
def __contains__(self, x: Any, /) -> bool: ...
|
||||
def __getitem__(self, key: _KT_contra, /) -> _VT_co: ...
|
||||
|
||||
# stable
|
||||
class SupportsContainsAndGetItem(Protocol[_KT_contra, _VT_co]):
|
||||
def __contains__(self, x: Any, /) -> bool: ...
|
||||
def __getitem__(self, key: _KT_contra, /) -> _VT_co: ...
|
||||
|
||||
# stable
|
||||
class SupportsItemAccess(Protocol[_KT_contra, _VT]):
|
||||
def __contains__(self, x: Any, /) -> bool: ...
|
||||
def __getitem__(self, key: _KT_contra, /) -> _VT: ...
|
||||
def __setitem__(self, key: _KT_contra, value: _VT, /) -> None: ...
|
||||
def __delitem__(self, key: _KT_contra, /) -> None: ...
|
||||
|
||||
StrPath: TypeAlias = str | PathLike[str] # stable
|
||||
BytesPath: TypeAlias = bytes | PathLike[bytes] # stable
|
||||
GenericPath: TypeAlias = AnyStr | PathLike[AnyStr]
|
||||
StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] # stable
|
||||
|
||||
OpenTextModeUpdating: TypeAlias = Literal[
|
||||
"r+",
|
||||
"+r",
|
||||
"rt+",
|
||||
"r+t",
|
||||
"+rt",
|
||||
"tr+",
|
||||
"t+r",
|
||||
"+tr",
|
||||
"w+",
|
||||
"+w",
|
||||
"wt+",
|
||||
"w+t",
|
||||
"+wt",
|
||||
"tw+",
|
||||
"t+w",
|
||||
"+tw",
|
||||
"a+",
|
||||
"+a",
|
||||
"at+",
|
||||
"a+t",
|
||||
"+at",
|
||||
"ta+",
|
||||
"t+a",
|
||||
"+ta",
|
||||
"x+",
|
||||
"+x",
|
||||
"xt+",
|
||||
"x+t",
|
||||
"+xt",
|
||||
"tx+",
|
||||
"t+x",
|
||||
"+tx",
|
||||
]
|
||||
OpenTextModeWriting: TypeAlias = Literal["w", "wt", "tw", "a", "at", "ta", "x", "xt", "tx"]
|
||||
OpenTextModeReading: TypeAlias = Literal["r", "rt", "tr", "U", "rU", "Ur", "rtU", "rUt", "Urt", "trU", "tUr", "Utr"]
|
||||
OpenTextMode: TypeAlias = OpenTextModeUpdating | OpenTextModeWriting | OpenTextModeReading
|
||||
OpenBinaryModeUpdating: TypeAlias = Literal[
|
||||
"rb+",
|
||||
"r+b",
|
||||
"+rb",
|
||||
"br+",
|
||||
"b+r",
|
||||
"+br",
|
||||
"wb+",
|
||||
"w+b",
|
||||
"+wb",
|
||||
"bw+",
|
||||
"b+w",
|
||||
"+bw",
|
||||
"ab+",
|
||||
"a+b",
|
||||
"+ab",
|
||||
"ba+",
|
||||
"b+a",
|
||||
"+ba",
|
||||
"xb+",
|
||||
"x+b",
|
||||
"+xb",
|
||||
"bx+",
|
||||
"b+x",
|
||||
"+bx",
|
||||
]
|
||||
OpenBinaryModeWriting: TypeAlias = Literal["wb", "bw", "ab", "ba", "xb", "bx"]
|
||||
OpenBinaryModeReading: TypeAlias = Literal["rb", "br", "rbU", "rUb", "Urb", "brU", "bUr", "Ubr"]
|
||||
OpenBinaryMode: TypeAlias = OpenBinaryModeUpdating | OpenBinaryModeReading | OpenBinaryModeWriting
|
||||
|
||||
# stable
|
||||
class HasFileno(Protocol):
|
||||
def fileno(self) -> int: ...
|
||||
|
||||
FileDescriptor: TypeAlias = int # stable
|
||||
FileDescriptorLike: TypeAlias = int | HasFileno # stable
|
||||
FileDescriptorOrPath: TypeAlias = int | StrOrBytesPath
|
||||
|
||||
# stable
|
||||
class SupportsRead(Protocol[_T_co]):
|
||||
def read(self, length: int = ..., /) -> _T_co: ...
|
||||
|
||||
# stable
|
||||
class SupportsReadline(Protocol[_T_co]):
|
||||
def readline(self, length: int = ..., /) -> _T_co: ...
|
||||
|
||||
# stable
|
||||
class SupportsNoArgReadline(Protocol[_T_co]):
|
||||
def readline(self) -> _T_co: ...
|
||||
|
||||
# stable
|
||||
class SupportsWrite(Protocol[_T_contra]):
|
||||
def write(self, s: _T_contra, /) -> object: ...
|
||||
|
||||
# stable
|
||||
class SupportsFlush(Protocol):
|
||||
def flush(self) -> object: ...
|
||||
|
||||
# Unfortunately PEP 688 does not allow us to distinguish read-only
|
||||
# from writable buffers. We use these aliases for readability for now.
|
||||
# Perhaps a future extension of the buffer protocol will allow us to
|
||||
# distinguish these cases in the type system.
|
||||
ReadOnlyBuffer: TypeAlias = Buffer # stable
|
||||
# Anything that implements the read-write buffer interface.
|
||||
WriteableBuffer: TypeAlias = Buffer
|
||||
# Same as WriteableBuffer, but also includes read-only buffer types (like bytes).
|
||||
ReadableBuffer: TypeAlias = Buffer # stable
|
||||
|
||||
class SliceableBuffer(Buffer, Protocol):
|
||||
def __getitem__(self, slice: slice, /) -> Sequence[int]: ...
|
||||
|
||||
class IndexableBuffer(Buffer, Protocol):
|
||||
def __getitem__(self, i: int, /) -> int: ...
|
||||
|
||||
class SupportsGetItemBuffer(SliceableBuffer, IndexableBuffer, Protocol):
|
||||
def __contains__(self, x: Any, /) -> bool: ...
|
||||
@overload
|
||||
def __getitem__(self, slice: slice, /) -> Sequence[int]: ...
|
||||
@overload
|
||||
def __getitem__(self, i: int, /) -> int: ...
|
||||
|
||||
class SizedBuffer(Sized, Buffer, Protocol): ...
|
||||
|
||||
# for compatibility with third-party stubs that may use this
|
||||
_BufferWithLen: TypeAlias = SizedBuffer # not stable # noqa: Y047
|
||||
|
||||
ExcInfo: TypeAlias = tuple[type[BaseException], BaseException, TracebackType]
|
||||
OptExcInfo: TypeAlias = ExcInfo | tuple[None, None, None]
|
||||
|
||||
# stable
|
||||
if sys.version_info >= (3, 10):
|
||||
from types import NoneType as NoneType
|
||||
else:
|
||||
# Used by type checkers for checks involving None (does not exist at runtime)
|
||||
@final
|
||||
class NoneType:
|
||||
def __bool__(self) -> Literal[False]: ...
|
||||
|
||||
# This is an internal CPython type that is like, but subtly different from, a NamedTuple
|
||||
# Subclasses of this type are found in multiple modules.
|
||||
# In typeshed, `structseq` is only ever used as a mixin in combination with a fixed-length `Tuple`
|
||||
# See discussion at #6546 & #6560
|
||||
# `structseq` classes are unsubclassable, so are all decorated with `@final`.
|
||||
class structseq(Generic[_T_co]):
|
||||
n_fields: Final[int]
|
||||
n_unnamed_fields: Final[int]
|
||||
n_sequence_fields: Final[int]
|
||||
# The first parameter will generally only take an iterable of a specific length.
|
||||
# E.g. `os.uname_result` takes any iterable of length exactly 5.
|
||||
#
|
||||
# The second parameter will accept a dict of any kind without raising an exception,
|
||||
# but only has any meaning if you supply it a dict where the keys are strings.
|
||||
# https://github.com/python/typeshed/pull/6560#discussion_r767149830
|
||||
def __new__(cls: type[Self], sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> Self: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def __replace__(self: Self, **kwargs: Any) -> Self: ...
|
||||
|
||||
# Superset of typing.AnyStr that also includes LiteralString
|
||||
AnyOrLiteralStr = TypeVar("AnyOrLiteralStr", str, bytes, LiteralString) # noqa: Y001
|
||||
|
||||
# Represents when str or LiteralStr is acceptable. Useful for string processing
|
||||
# APIs where literalness of return value depends on literalness of inputs
|
||||
StrOrLiteralStr = TypeVar("StrOrLiteralStr", LiteralString, str) # noqa: Y001
|
||||
|
||||
# Objects suitable to be passed to sys.setprofile, threading.setprofile, and similar
|
||||
ProfileFunction: TypeAlias = Callable[[FrameType, str, Any], object]
|
||||
|
||||
# Objects suitable to be passed to sys.settrace, threading.settrace, and similar
|
||||
TraceFunction: TypeAlias = Callable[[FrameType, str, Any], TraceFunction | None]
|
||||
|
||||
# experimental
|
||||
# Might not work as expected for pyright, see
|
||||
# https://github.com/python/typeshed/pull/9362
|
||||
# https://github.com/microsoft/pyright/issues/4339
|
||||
class DataclassInstance(Protocol):
|
||||
__dataclass_fields__: ClassVar[dict[str, Field[Any]]]
|
||||
|
||||
# Anything that can be passed to the int/float constructors
|
||||
ConvertibleToInt: TypeAlias = str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc
|
||||
ConvertibleToFloat: TypeAlias = str | ReadableBuffer | SupportsFloat | SupportsIndex
|
||||
|
||||
# A few classes updated from Foo(str, Enum) to Foo(StrEnum). This is a convenience so these
|
||||
# can be accurate on all python versions without getting too wordy
|
||||
if sys.version_info >= (3, 11):
|
||||
from enum import StrEnum as StrEnum
|
||||
else:
|
||||
from enum import Enum
|
||||
|
||||
class StrEnum(str, Enum): ...
|
||||
@@ -1,37 +0,0 @@
|
||||
# PEP 249 Database API 2.0 Types
|
||||
# https://www.python.org/dev/peps/pep-0249/
|
||||
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Any, Protocol
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
DBAPITypeCode: TypeAlias = Any | None
|
||||
# Strictly speaking, this should be a Sequence, but the type system does
|
||||
# not support fixed-length sequences.
|
||||
DBAPIColumnDescription: TypeAlias = tuple[str, DBAPITypeCode, int | None, int | None, int | None, int | None, bool | None]
|
||||
|
||||
class DBAPIConnection(Protocol):
|
||||
def close(self) -> object: ...
|
||||
def commit(self) -> object: ...
|
||||
# optional:
|
||||
# def rollback(self) -> Any: ...
|
||||
def cursor(self) -> DBAPICursor: ...
|
||||
|
||||
class DBAPICursor(Protocol):
|
||||
@property
|
||||
def description(self) -> Sequence[DBAPIColumnDescription] | None: ...
|
||||
@property
|
||||
def rowcount(self) -> int: ...
|
||||
# optional:
|
||||
# def callproc(self, procname: str, parameters: Sequence[Any] = ..., /) -> Sequence[Any]: ...
|
||||
def close(self) -> object: ...
|
||||
def execute(self, operation: str, parameters: Sequence[Any] | Mapping[str, Any] = ..., /) -> object: ...
|
||||
def executemany(self, operation: str, seq_of_parameters: Sequence[Sequence[Any]], /) -> object: ...
|
||||
def fetchone(self) -> Sequence[Any] | None: ...
|
||||
def fetchmany(self, size: int = ..., /) -> Sequence[Sequence[Any]]: ...
|
||||
def fetchall(self) -> Sequence[Sequence[Any]]: ...
|
||||
# optional:
|
||||
# def nextset(self) -> None | Literal[True]: ...
|
||||
arraysize: int
|
||||
def setinputsizes(self, sizes: Sequence[DBAPITypeCode | int | None], /) -> object: ...
|
||||
def setoutputsize(self, size: int, column: int = ..., /) -> object: ...
|
||||
@@ -1,18 +0,0 @@
|
||||
# Implicit protocols used in importlib.
|
||||
# We intentionally omit deprecated and optional methods.
|
||||
|
||||
from collections.abc import Sequence
|
||||
from importlib.machinery import ModuleSpec
|
||||
from types import ModuleType
|
||||
from typing import Protocol
|
||||
|
||||
__all__ = ["LoaderProtocol", "MetaPathFinderProtocol", "PathEntryFinderProtocol"]
|
||||
|
||||
class LoaderProtocol(Protocol):
|
||||
def load_module(self, fullname: str, /) -> ModuleType: ...
|
||||
|
||||
class MetaPathFinderProtocol(Protocol):
|
||||
def find_spec(self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = ..., /) -> ModuleSpec | None: ...
|
||||
|
||||
class PathEntryFinderProtocol(Protocol):
|
||||
def find_spec(self, fullname: str, target: ModuleType | None = ..., /) -> ModuleSpec | None: ...
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user