mirror of
https://github.com/davidhalter/typeshed.git
synced 2026-06-15 04:00:43 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74b5289e33 |
@@ -1,16 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
max_line_length = 79
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{py,pyi,toml,json}]
|
||||
max_line_length = 130
|
||||
indent_size = 4
|
||||
@@ -1,14 +1,23 @@
|
||||
[flake8]
|
||||
# Y: Flake8 is only used to run flake8-pyi, everything else is in Ruff
|
||||
select = Y
|
||||
# Ignore rules normally excluded by default
|
||||
extend-ignore = Y090,Y091
|
||||
per-file-ignores =
|
||||
# Generated protobuf files:
|
||||
# Y021: Include docstrings
|
||||
# Y023: Alias typing as typing_extensions
|
||||
# Y026: Have implicit type aliases
|
||||
# Y053: have literals >50 characters long
|
||||
stubs/*_pb2.pyi: Y021, Y023, Y026, Y053
|
||||
# Some PEP8 deviations are considered irrelevant to stub files:
|
||||
# (error counts as of 2017-05-22)
|
||||
# 17952 E704 multiple statements on one line (def)
|
||||
# 12197 E301 expected 1 blank line
|
||||
# 7155 E302 expected 2 blank lines
|
||||
# 1463 F401 imported but unused
|
||||
# 967 E701 multiple statements on one line (colon)
|
||||
# 457 F811 redefinition (should be fixed in pyflakes 2.1.2)
|
||||
# 390 E305 expected 2 blank lines
|
||||
# 4 E741 ambiguous variable name
|
||||
|
||||
exclude = .venv*,.git
|
||||
# Nice-to-haves ignored for now
|
||||
# 2307 E501 line too long
|
||||
|
||||
# Other ignored warnings
|
||||
# W504 line break after binary operator
|
||||
|
||||
[flake8]
|
||||
ignore = F401, F403, F405, F811, E301, E302, E305, E501, E701, E704, E741, B303, W504
|
||||
# We are checking with Python 3 but many of the stubs are Python 2 stubs.
|
||||
builtins = StandardError,apply,basestring,buffer,cmp,coerce,execfile,file,intern,long,raw_input,reduce,reload,unichr,unicode,xrange
|
||||
exclude = .venv*,@*,.git
|
||||
max-line-length = 130
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Normalize EOF
|
||||
* autocrlf=false
|
||||
* eol=lf
|
||||
# Set linguist-language to support comments syntax highlight
|
||||
**/stubtest_allowlist*.txt linguist-language=ini
|
||||
**/stubtest_allowlists/*.txt linguist-language=ini
|
||||
pyrightconfig*.json linguist-language=jsonc
|
||||
.vscode/*.json linguist-language=jsonc
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"dependencyDashboard": true,
|
||||
"suppressNotifications": ["prEditedNotification"],
|
||||
"extends": ["config:recommended"],
|
||||
"labels": ["bot: dependencies"],
|
||||
"rebaseLabel": ["bot: rebase"],
|
||||
"semanticCommits": "disabled",
|
||||
"separateMajorMinor": false,
|
||||
"prHourlyLimit": 10,
|
||||
// This package rule disables updates for `actions/setup-python` Python versions:
|
||||
// it's better to do these manually as there's often a reason why we can't use
|
||||
// the latest Python version in CI for a specific job
|
||||
ignoreDeps: ["python"],
|
||||
"pre-commit": {
|
||||
"enabled": true
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
groupName: "GitHub Actions",
|
||||
matchManagers: ["github-actions"],
|
||||
description: "Quarterly update of GitHub Action dependencies",
|
||||
schedule: ["every 3 months on the first day of the month"]
|
||||
},
|
||||
{
|
||||
groupName: "most test/lint dependencies",
|
||||
matchManagers: ["pip_requirements", "pre-commit"],
|
||||
matchPackageNames: ["!pyright"],
|
||||
description: "Quarterly update of most test dependencies",
|
||||
schedule: ["every 3 months on the first day of the month"]
|
||||
},
|
||||
{
|
||||
"groupName": "pyright",
|
||||
"matchManagers": ["pip_requirements"],
|
||||
"matchPackageNames": ["pyright"],
|
||||
"description": "Daily update of pyright",
|
||||
"schedule": ["before 4am"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
name: Daily test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
pull_request:
|
||||
paths:
|
||||
- "requirements-tests.txt"
|
||||
- ".github/workflows/daily.yml"
|
||||
|
||||
# Please keep the permissions minimal, as stubtest runs arbitrary code from pypi.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# A few env vars to speedup brew install
|
||||
HOMEBREW_NO_ANALYTICS: 1
|
||||
HOMEBREW_NO_AUTOUPDATE: 1
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1 # Environments are isolated, no need to cleanup old versions
|
||||
NONINTERACTIVE: 1 # Required for brew install on CI
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
FORCE_COLOR: 1
|
||||
TERM: xterm-256color # needed for FORCE_COLOR to work on mypy on Ubuntu, see https://github.com/python/mypy/issues/13817
|
||||
|
||||
jobs:
|
||||
stubtest-stdlib:
|
||||
name: "stubtest: stdlib"
|
||||
if: ${{ github.repository == 'python/typeshed' || github.event_name != 'schedule' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: pip
|
||||
cache-dependency-path: requirements-tests.txt
|
||||
allow-prereleases: true
|
||||
check-latest: true
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements-tests.txt
|
||||
- name: Run stubtest
|
||||
run: python tests/stubtest_stdlib.py
|
||||
|
||||
stubtest-third-party:
|
||||
name: "stubtest: third party"
|
||||
if: ${{ github.repository == 'python/typeshed' || github.event_name != 'schedule' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
|
||||
shard-index: [0, 1, 2, 3]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
requirements-tests.txt
|
||||
stubs/**/METADATA.toml
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements-tests.txt
|
||||
- name: Install required system packages
|
||||
shell: bash
|
||||
run: |
|
||||
PACKAGES=$(python tests/get_stubtest_system_requirements.py)
|
||||
|
||||
if [ "${{ runner.os }}" = "Linux" ]; then
|
||||
if [ -n "$PACKAGES" ]; then
|
||||
printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
sudo apt-get update -q && sudo apt-get install -qy $PACKAGES
|
||||
fi
|
||||
else
|
||||
if [ "${{ runner.os }}" = "macOS" ] && [ -n "$PACKAGES" ]; then
|
||||
printf "Installing Homebrew packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
brew install -q $PACKAGES
|
||||
fi
|
||||
|
||||
if [ "${{ runner.os }}" = "Windows" ] && [ -n "$PACKAGES" ]; then
|
||||
printf "Installing Chocolatey packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
choco install -y $PACKAGES
|
||||
fi
|
||||
fi
|
||||
- name: Run stubtest
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ runner.os }}" = "Linux" ]; then
|
||||
PYTHON_EXECUTABLE="xvfb-run python"
|
||||
else
|
||||
PYTHON_EXECUTABLE="python"
|
||||
fi
|
||||
|
||||
$PYTHON_EXECUTABLE tests/stubtest_third_party.py --ci-platforms-only --num-shards 4 --shard-index ${{ matrix.shard-index }}
|
||||
|
||||
stub-uploader:
|
||||
name: stub_uploader tests
|
||||
if: ${{ github.repository == 'python/typeshed' || github.event_name != 'schedule' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout typeshed
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: typeshed
|
||||
- name: Checkout stub_uploader
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: typeshed-internal/stub_uploader
|
||||
path: stub_uploader
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
# Keep in sync with stub_uploader's check_scripts.yml workflow.
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "typeshed/requirements-tests.txt"
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd stub_uploader
|
||||
uv pip install -r requirements.txt --system
|
||||
python -m pytest tests
|
||||
|
||||
# https://github.community/t/run-github-actions-job-only-if-previous-job-has-failed/174786/2
|
||||
create-issue-on-failure:
|
||||
name: Create issue on failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [stubtest-stdlib, stubtest-third-party, stub-uploader]
|
||||
if: ${{ github.repository == 'python/typeshed' && always() && github.event_name == 'schedule' && (needs.stubtest-stdlib.result == 'failure' || needs.stubtest-third-party.result == 'failure' || needs.stub-uploader.result == 'failure') }}
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.create({
|
||||
owner: "python",
|
||||
repo: "typeshed",
|
||||
title: `Daily tests failed on ${new Date().toDateString()}`,
|
||||
body: "Run listed here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
|
||||
labels: ["help wanted"],
|
||||
})
|
||||
@@ -1,86 +0,0 @@
|
||||
# This workflow is for testing typeshed's scripts and tests themselves
|
||||
name: Meta-tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths:
|
||||
- "scripts/**"
|
||||
- "tests/**"
|
||||
- "lib/**"
|
||||
- ".github/workflows/meta_tests.yml"
|
||||
- "requirements-tests.txt"
|
||||
- "pyproject.toml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
FORCE_COLOR: 1
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
mypy:
|
||||
name: Check scripts and tests with mypy
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
platform: ["linux", "win32"]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- run: uv pip install -r requirements-tests.txt --system
|
||||
- run: python ./tests/typecheck_typeshed.py --platform=${{ matrix.platform }}
|
||||
pyright:
|
||||
name: Check scripts and tests with pyright
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-platform: ["Linux", "Windows"]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- run: uv pip install -r requirements-tests.txt --system
|
||||
- name: Run pyright on typeshed
|
||||
uses: jakebailey/pyright-action@v3
|
||||
with:
|
||||
version: PATH
|
||||
python-platform: ${{ matrix.python-platform }}
|
||||
python-version: "3.10" # Oldest version supported for running scripts and tests
|
||||
project: ./pyrightconfig.scripts_and_tests.json
|
||||
stubsabot-dry-run:
|
||||
name: Stubsabot dry run
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- name: Git config
|
||||
run: |
|
||||
git config --global user.name stubsabot
|
||||
git config --global user.email '<>'
|
||||
- run: uv pip install -r requirements-tests.txt --system
|
||||
- run: python scripts/stubsabot.py --action-level local
|
||||
@@ -1,91 +0,0 @@
|
||||
name: Run mypy_primer
|
||||
|
||||
on:
|
||||
# Only run on PR, since we diff against main
|
||||
pull_request:
|
||||
paths:
|
||||
- "stdlib/**"
|
||||
- "stubs/**/*.pyi"
|
||||
- ".github/workflows/mypy_primer.yml"
|
||||
- ".github/workflows/mypy_primer_comment.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
mypy_primer:
|
||||
name: Run
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
shard-index: [0, 1, 2, 3]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
path: typeshed_to_test
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- name: Install dependencies
|
||||
run: pip install git+https://github.com/hauntsaninja/mypy_primer.git
|
||||
- name: Run mypy_primer
|
||||
shell: bash
|
||||
run: |
|
||||
cd typeshed_to_test
|
||||
MYPY_VERSION=$(grep mypy== requirements-tests.txt | cut -d = -f 3)
|
||||
echo "new commit"
|
||||
git rev-list --format=%s --max-count=1 $GITHUB_SHA
|
||||
git checkout -b upstream_main origin/main
|
||||
echo "base commit"
|
||||
git rev-list --format=%s --max-count=1 upstream_main
|
||||
echo ''
|
||||
cd ..
|
||||
# fail action if exit code isn't zero or one
|
||||
(
|
||||
mypy_primer \
|
||||
--new v${MYPY_VERSION} --old v${MYPY_VERSION} \
|
||||
--custom-typeshed-repo typeshed_to_test \
|
||||
--new-typeshed $GITHUB_SHA --old-typeshed upstream_main \
|
||||
--num-shards 4 --shard-index ${{ matrix.shard-index }} \
|
||||
--debug \
|
||||
--output concise \
|
||||
| tee diff_${{ matrix.shard-index }}.txt
|
||||
) || [ $? -eq 1 ]
|
||||
- if: ${{ matrix.shard-index == 0 }}
|
||||
name: Save PR number
|
||||
run: |
|
||||
echo ${{ github.event.pull_request.number }} | tee pr_number.txt
|
||||
- name: Upload mypy_primer diff + PR number
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ matrix.shard-index == 0 }}
|
||||
with:
|
||||
name: mypy_primer_diffs-${{ matrix.shard-index }}
|
||||
path: |
|
||||
diff_${{ matrix.shard-index }}.txt
|
||||
pr_number.txt
|
||||
- name: Upload mypy_primer diff
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ matrix.shard-index != 0 }}
|
||||
with:
|
||||
name: mypy_primer_diffs-${{ matrix.shard-index }}
|
||||
path: diff_${{ matrix.shard-index }}.txt
|
||||
|
||||
join_artifacts:
|
||||
name: Join artifacts
|
||||
runs-on: ubuntu-latest
|
||||
needs: [mypy_primer]
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Merge artifacts
|
||||
uses: actions/upload-artifact/merge@v7
|
||||
with:
|
||||
name: mypy_primer_diffs
|
||||
pattern: mypy_primer_diffs-*
|
||||
delete-merged: true
|
||||
@@ -1,87 +0,0 @@
|
||||
name: Comment with mypy_primer diff
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- Run mypy_primer
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
name: Comment PR from mypy_primer
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Download diffs
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.event.workflow_run.id }},
|
||||
});
|
||||
const [matchArtifact] = artifacts.data.artifacts.filter((artifact) =>
|
||||
artifact.name == "mypy_primer_diffs");
|
||||
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: "zip",
|
||||
});
|
||||
fs.writeFileSync("diff.zip", Buffer.from(download.data));
|
||||
|
||||
- run: unzip diff.zip
|
||||
- run: |
|
||||
cat diff_*.txt | tee fulldiff.txt
|
||||
|
||||
- name: Post comment
|
||||
id: post-comment
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
let data = fs.readFileSync('fulldiff.txt', { encoding: 'utf8' })
|
||||
|
||||
// Maximum comment length is 65536 characters. We need much less than 236 for extra text.
|
||||
const MAX_LENGTH = 65300
|
||||
if (data.length > MAX_LENGTH) {
|
||||
let truncated_data = data.substring(0, MAX_LENGTH)
|
||||
let lines_truncated = data.split('\n').length - truncated_data.split('\n').length
|
||||
data = truncated_data + `\n\n... (truncated ${lines_truncated} lines) ...\n`
|
||||
}
|
||||
|
||||
console.log("Diff from mypy_primer:")
|
||||
console.log(data)
|
||||
|
||||
let body
|
||||
if (data.trim()) {
|
||||
body = 'Diff from [mypy_primer](https://github.com/hauntsaninja/mypy_primer), showing the effect of this PR on open source code:\n```diff\n' + data + '```'
|
||||
} else {
|
||||
body = 'According to [mypy_primer](https://github.com/hauntsaninja/mypy_primer), this change has no effect on the checked open source code. 🤖🎉'
|
||||
}
|
||||
|
||||
const prNumber = parseInt(fs.readFileSync("pr_number.txt", { encoding: "utf8" }))
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body
|
||||
})
|
||||
return prNumber
|
||||
|
||||
- name: Hide old comments
|
||||
# v0.4.0
|
||||
uses: kanga333/comment-hider@c12bb20b48aeb8fc098e35967de8d4f8018fffdf
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
leave_visible: 1
|
||||
issue_number: ${{ steps.post-comment.outputs.result }}
|
||||
@@ -1,58 +0,0 @@
|
||||
name: Run stubsabot daily
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
stubsabot:
|
||||
name: Upgrade stubs with stubsabot
|
||||
if: github.repository == 'python/typeshed'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
# use an ssh key so that checks automatically run on stubsabot PRs
|
||||
ssh-key: ${{ secrets.STUBSABOT_SSH_PRIVATE_KEY }}
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- name: git config
|
||||
run: |
|
||||
git config --global user.name stubsabot
|
||||
git config --global user.email '<>'
|
||||
- name: Install dependencies
|
||||
run: uv pip install -r requirements-tests.txt --system
|
||||
- name: Run stubsabot
|
||||
run: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} python scripts/stubsabot.py --action-level everything
|
||||
|
||||
# https://github.community/t/run-github-actions-job-only-if-previous-job-has-failed/174786/2
|
||||
create-issue-on-failure:
|
||||
name: Create issue on failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [stubsabot]
|
||||
if: ${{ github.repository == 'python/typeshed' && always() && (needs.stubsabot.result == 'failure') }}
|
||||
steps:
|
||||
- uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.create({
|
||||
owner: "python",
|
||||
repo: "typeshed",
|
||||
title: `Stubsabot failed on ${new Date().toDateString()}`,
|
||||
body: "Stubsabot run is listed here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
|
||||
})
|
||||
@@ -1,50 +0,0 @@
|
||||
name: Stdlib stubtest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths:
|
||||
- "stdlib/**"
|
||||
- ".github/workflows/stubtest_stdlib.yml"
|
||||
- "tests/**"
|
||||
# When requirements.txt changes, we run `daily.yml`, which includes stdlib stubtest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
FORCE_COLOR: 1
|
||||
TERM: xterm-256color # needed for FORCE_COLOR to work on mypy on Ubuntu, see https://github.com/python/mypy/issues/13817
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
stubtest-stdlib:
|
||||
name: "stubtest: stdlib"
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: pip
|
||||
cache-dependency-path: requirements-tests.txt
|
||||
allow-prereleases: true
|
||||
check-latest: true
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements-tests.txt
|
||||
- name: Run stubtest
|
||||
run: python tests/stubtest_stdlib.py
|
||||
@@ -1,101 +0,0 @@
|
||||
name: Third-party stubtest
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "stubs/**"
|
||||
- ".github/workflows/stubtest_third_party.yml"
|
||||
- "tests/**"
|
||||
# When requirements.txt changes, we run `daily.yml`, which includes third-party stubtest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# A few env vars to speedup brew install
|
||||
HOMEBREW_NO_ANALYTICS: 1
|
||||
HOMEBREW_NO_AUTOUPDATE: 1
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1 # Environments are isolated, no need to cleanup old versions
|
||||
NONINTERACTIVE: 1 # Required for brew install on CI
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
FORCE_COLOR: 1
|
||||
TERM: xterm-256color # needed for FORCE_COLOR to work on mypy on Ubuntu, see https://github.com/python/mypy/issues/13817
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
stubtest-third-party:
|
||||
name: "stubtest: third party"
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
requirements-tests.txt
|
||||
stubs/**/METADATA.toml
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements-tests.txt
|
||||
- name: Determine changed stubs
|
||||
shell: bash
|
||||
run: |
|
||||
# This only runs stubtest on changed stubs, because it is much faster.
|
||||
# Use the daily.yml workflow to run stubtest on all third party stubs.
|
||||
function find_stubs {
|
||||
git diff --name-only origin/${{ github.base_ref }} HEAD | \
|
||||
egrep ^stubs/ | cut -d "/" -f 2 | sort -u | \
|
||||
(while read stub; do [ -d "stubs/$stub" ] && echo -n "$stub " || true; done)
|
||||
}
|
||||
STUBS=$(find_stubs || echo '')
|
||||
echo "Changed stubs: $STUBS"
|
||||
echo "STUBS=$STUBS" >> $GITHUB_ENV
|
||||
- name: Install required system packages
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$STUBS" ]; then
|
||||
PACKAGES=$(python tests/get_stubtest_system_requirements.py $STUBS)
|
||||
if [ "${{ runner.os }}" = "Linux" ]; then
|
||||
if [ -n "$PACKAGES" ]; then
|
||||
printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
sudo apt-get update -q && sudo apt-get install -qy $PACKAGES
|
||||
fi
|
||||
else
|
||||
if [ "${{ runner.os }}" = "macOS" ] && [ -n "$PACKAGES" ]; then
|
||||
printf "Installing Homebrew packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
brew install -q $PACKAGES
|
||||
fi
|
||||
|
||||
if [ "${{ runner.os }}" = "Windows" ] && [ -n "$PACKAGES" ]; then
|
||||
printf "Installing Chocolatey packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
choco install -y $PACKAGES
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
- name: Run stubtest
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$STUBS" ]; then
|
||||
echo "Testing $STUBS..."
|
||||
|
||||
if [ "${{ runner.os }}" = "Linux" ]; then
|
||||
PYTHON_EXECUTABLE="xvfb-run python"
|
||||
else
|
||||
PYTHON_EXECUTABLE="python"
|
||||
fi
|
||||
|
||||
$PYTHON_EXECUTABLE tests/stubtest_third_party.py --ci-platforms-only $STUBS
|
||||
else
|
||||
echo "Nothing to test"
|
||||
fi
|
||||
@@ -1,168 +0,0 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**/*.md"
|
||||
- "scripts/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
FORCE_COLOR: 1
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
typeshed-structure:
|
||||
name: Check typeshed structure
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- run: uv pip install -r requirements-tests.txt --system
|
||||
- run: python ./tests/check_typeshed_structure.py
|
||||
|
||||
mypy:
|
||||
name: "mypy: Check stubs"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
platform: ["linux", "win32", "darwin"]
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
allow-prereleases: true
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- run: uv pip install -r requirements-tests.txt --system
|
||||
- name: Install required APT packages
|
||||
run: |
|
||||
PACKAGES=$(python tests/get_stubtest_system_requirements.py)
|
||||
if [ -n "$PACKAGES" ]; then
|
||||
printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
sudo apt-get update -q && sudo apt-get install -qy $PACKAGES
|
||||
fi
|
||||
- name: Run mypy_test.py
|
||||
run: python ./tests/mypy_test.py --platform=${{ matrix.platform }} --python-version=${{ matrix.python-version }}
|
||||
|
||||
regression-tests:
|
||||
name: "mypy: Run test cases"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.14"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- run: uv pip install -r requirements-tests.txt --system
|
||||
- run: python ./tests/regr_test.py --all --verbosity QUIET
|
||||
|
||||
pyright:
|
||||
name: "pyright: Run test cases"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-platform: ["Linux", "Windows", "Darwin"]
|
||||
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "requirements-tests.txt"
|
||||
- name: Install typeshed test-suite requirements
|
||||
# Install these so we can run `get_*_requirements.py`
|
||||
run: uv pip install -r requirements-tests.txt --system
|
||||
- name: Install required APT packages
|
||||
run: |
|
||||
PACKAGES=$(python tests/get_stubtest_system_requirements.py)
|
||||
if [ -n "$PACKAGES" ]; then
|
||||
printf "Installing APT packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
sudo apt-get update -q && sudo apt-get install -qy $PACKAGES
|
||||
fi
|
||||
- name: Create an isolated venv for testing
|
||||
run: uv venv .venv
|
||||
- name: Install 3rd-party stub dependencies
|
||||
run: |
|
||||
PACKAGES=$(python tests/get_external_stub_requirements.py)
|
||||
if [ -n "$PACKAGES" ]; then
|
||||
printf "Installing python packages:\n $(echo $PACKAGES | sed 's/ /\n /g')\n"
|
||||
uv pip install --python-version ${{ matrix.python-version }} $PACKAGES
|
||||
fi
|
||||
- name: Activate the isolated venv for the rest of the job
|
||||
run: echo "$PWD/.venv/bin" >> $GITHUB_PATH
|
||||
- name: List 3rd-party stub dependencies installed
|
||||
run: uv pip freeze
|
||||
- name: Run pyright with basic settings on all the stubs
|
||||
uses: jakebailey/pyright-action@v3
|
||||
with:
|
||||
version: PATH
|
||||
python-platform: ${{ matrix.python-platform }}
|
||||
python-version: ${{ matrix.python-version }}
|
||||
annotate: ${{ matrix.python-version == '3.13' && matrix.python-platform == 'Linux' }} # Having each job create the same comment is too noisy.
|
||||
- name: Run pyright with stricter settings on some of the stubs
|
||||
uses: jakebailey/pyright-action@v3
|
||||
with:
|
||||
version: PATH
|
||||
python-platform: ${{ matrix.python-platform }}
|
||||
python-version: ${{ matrix.python-version }}
|
||||
annotate: ${{ matrix.python-version == '3.13' && matrix.python-platform == 'Linux' }} # Having each job create the same comment is too noisy.
|
||||
project: ./pyrightconfig.stricter.json
|
||||
- name: Run pyright on the test cases
|
||||
uses: jakebailey/pyright-action@v3
|
||||
with:
|
||||
version: PATH
|
||||
python-platform: ${{ matrix.python-platform }}
|
||||
python-version: ${{ matrix.python-version }}
|
||||
annotate: ${{ matrix.python-version == '3.13' && matrix.python-platform == 'Linux' }} # Having each job create the same comment is too noisy.
|
||||
project: ./pyrightconfig.testcases.json
|
||||
|
||||
stub-uploader:
|
||||
name: stub_uploader tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout typeshed
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: typeshed
|
||||
- name: Checkout stub_uploader
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: typeshed-internal/stub_uploader
|
||||
path: stub_uploader
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
# Keep in sync with stub_uploader's check_scripts.yml workflow.
|
||||
python-version: "3.13"
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version-file: "typeshed/requirements-tests.txt"
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd stub_uploader
|
||||
uv pip install -r requirements.txt --system
|
||||
python -m pytest tests
|
||||
+12
-22
@@ -7,16 +7,17 @@ __pycache__/
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
/env/
|
||||
/lib/build/
|
||||
/develop-eggs/
|
||||
/dist/
|
||||
/downloads/
|
||||
/eggs/
|
||||
/lib64/
|
||||
/parts/
|
||||
/sdist/
|
||||
/var/
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
@@ -38,7 +39,6 @@ htmlcov/
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
stubtest-output*
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
@@ -59,19 +59,9 @@ analyze.py
|
||||
# Editor backup files
|
||||
*~
|
||||
.*.sw?
|
||||
.vscode/*
|
||||
!.vscode/settings.default.json
|
||||
!.vscode/extensions.json
|
||||
.vscode/
|
||||
.idea/
|
||||
.venv*/
|
||||
|
||||
# Mypy cache
|
||||
.mypy_cache/
|
||||
|
||||
# pyenv and uv local python version
|
||||
.python-version
|
||||
# we don't use uv's lock as we're not actually a project
|
||||
uv.lock
|
||||
|
||||
# deliberately local test configuration files
|
||||
stdlib/@tests/stubtest_allowlists/*.local
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-toml
|
||||
- id: check-merge-conflict
|
||||
- id: mixed-line-ending
|
||||
args: [--fix=lf]
|
||||
- id: check-case-conflict
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.9 # must match requirements-tests.txt
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: Run ruff on stubs, tests and scripts
|
||||
args: ["--exit-non-zero-on-fix"]
|
||||
- id: ruff
|
||||
# Very few rules are useful to run on our test cases;
|
||||
# we explicitly enumerate them here:
|
||||
name: Run ruff on the test cases
|
||||
args:
|
||||
- "--exit-non-zero-on-fix"
|
||||
- "--select=FA,I,ICN001,RUF100"
|
||||
- "--no-force-exclude"
|
||||
- "--unsafe-fixes"
|
||||
files: '.*test_cases/.+\.py$'
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 26.3.1
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 7.3.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
language: python
|
||||
additional_dependencies:
|
||||
- "flake8-pyi==25.5.0"
|
||||
types: [file]
|
||||
types_or: [python, pyi]
|
||||
- repo: meta
|
||||
hooks:
|
||||
- id: check-hooks-apply
|
||||
|
||||
ci:
|
||||
autofix_commit_msg: "[pre-commit.ci] auto fixes from pre-commit.com hooks"
|
||||
autofix_prs: true
|
||||
autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate"
|
||||
autoupdate_schedule: quarterly
|
||||
submodules: false
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
dist: bionic
|
||||
language: python
|
||||
python: 3.8
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: "pytype"
|
||||
python: 3.6
|
||||
install: pip install -r requirements-tests-py3.txt
|
||||
script: ./tests/pytype_test.py
|
||||
- name: "mypy (typed-ast)"
|
||||
python: 3.7
|
||||
install: pip install -U git+git://github.com/python/mypy git+git://github.com/python/typed_ast
|
||||
script: ./tests/mypy_test.py --platform=linux
|
||||
- name: "mypy (ast)"
|
||||
python: 3.8
|
||||
install: pip install -U git+git://github.com/python/mypy
|
||||
script: ./tests/mypy_test.py --platform=linux
|
||||
- name: "mypy (Windows)"
|
||||
install: pip install -U git+git://github.com/python/mypy
|
||||
script: ./tests/mypy_test.py --platform=win32
|
||||
- name: "mypy (Darwin)"
|
||||
install: pip install -U git+git://github.com/python/mypy
|
||||
script: ./tests/mypy_test.py --platform=darwin
|
||||
- name: "mypy self test"
|
||||
script: ./tests/mypy_selftest.py
|
||||
- name: "check file consistency"
|
||||
script: ./tests/check_consistent.py
|
||||
- name: "flake8"
|
||||
install: pip install -r requirements-tests-py3.txt
|
||||
script: flake8
|
||||
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
// ⚠ Disclaimer: The typeshed team doesn't commit to maintaining this file. It exists purely for your ease of use.
|
||||
// Keep in alphabetical order
|
||||
{
|
||||
"recommendations": [
|
||||
"bierner.github-markdown-preview",
|
||||
"charliermarsh.ruff",
|
||||
"editorconfig.editorconfig",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.flake8",
|
||||
"ms-python.mypy-type-checker",
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"tamasfe.even-better-toml",
|
||||
"redhat.vscode-yaml",
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
/*
|
||||
* Don't recommend by default for this workspace
|
||||
*/
|
||||
"christian-kohler.npm-intellisense",
|
||||
/*
|
||||
* Must disable in this workspace
|
||||
* https://github.com/microsoft/vscode/issues/40239
|
||||
*/
|
||||
// even-better-toml has format on save
|
||||
"bungcip.better-toml",
|
||||
// Don't use two mypy extensions simultaneously
|
||||
"matangover.mypy",
|
||||
// Use Ruff instead
|
||||
"ms-python.isort",
|
||||
// We use Black
|
||||
"ms-python.autopep8",
|
||||
// Not using pylint
|
||||
"ms-python.pylint",
|
||||
// VSCode has implemented an optimized version
|
||||
"coenraads.bracket-pair-colorizer",
|
||||
"coenraads.bracket-pair-colorizer-2",
|
||||
// Obsoleted by Pylance
|
||||
"ms-pyright.pyright",
|
||||
],
|
||||
}
|
||||
Vendored
-125
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copy this file as `.vscode/settings.json` to configure VSCode for this workspace.
|
||||
* Unfortunately, VSCode doesn't (yet) offer any way to have "workspace defaults" or "user-worspace settings",
|
||||
* so offering defaults to copy is the best we can do at the moment.
|
||||
*
|
||||
* ⚠ Disclaimer: The typeshed team doesn't commit to maintaining this file. It exists purely for your ease of use.
|
||||
*/
|
||||
{
|
||||
// Don't format on save for formatters we don't explicitely control
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "never"
|
||||
},
|
||||
// Set file associations to support comments syntax highlight
|
||||
"files.associations": {
|
||||
"settings.default.json": "jsonc",
|
||||
"pyrightconfig*.json": "jsonc",
|
||||
".flake8": "properties",
|
||||
"stubtest_allowlist*.txt": "properties",
|
||||
"**/stubtest_allowlists/*.txt": "properties",
|
||||
"pytype_exclude_list.txt": "properties"
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/.*_cache": true, // mypy and Ruff cache
|
||||
"**/__pycache__": true
|
||||
},
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"editor.comments.insertSpace": true,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.detectIndentation": false,
|
||||
"editor.tabSize": 2,
|
||||
"[json][jsonc][python][toml]": {
|
||||
"editor.tabSize": 4
|
||||
},
|
||||
"editor.rulers": [
|
||||
90,
|
||||
130
|
||||
],
|
||||
"[git-commit]": {
|
||||
"editor.rulers": [
|
||||
72
|
||||
]
|
||||
},
|
||||
// Format on save for formatters we explicitely control
|
||||
"[json][jsonc][yaml][python][toml]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "explicit"
|
||||
}
|
||||
},
|
||||
"[json][jsonc]": {
|
||||
"editor.defaultFormatter": "vscode.json-language-features"
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.defaultFormatter": "redhat.vscode-yaml"
|
||||
},
|
||||
"[toml]": {
|
||||
"editor.rulers": [
|
||||
90
|
||||
],
|
||||
"editor.defaultFormatter": "tamasfe.even-better-toml"
|
||||
},
|
||||
"[python]": {
|
||||
"editor.rulers": [
|
||||
130
|
||||
],
|
||||
"editor.defaultFormatter": "ms-python.black-formatter",
|
||||
"editor.codeActionsOnSave": {
|
||||
// Let Ruff lint fixes handle imports
|
||||
"source.organizeImports": "never",
|
||||
"source.unusedImports": "never"
|
||||
}
|
||||
},
|
||||
// python.analysis is Pylance (pyright) configurations
|
||||
"python.analysis.fixAll": [
|
||||
// Explicitly omiting "source.convertImportFormat", some stubs use relative imports
|
||||
// Explicitly omiting "source.unusedImports", Let Ruff lint fixes handle imports
|
||||
],
|
||||
"python.analysis.typeshedPaths": [
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"python.analysis.extraPaths": [
|
||||
"tests"
|
||||
],
|
||||
"mypy-type-checker.importStrategy": "fromEnvironment",
|
||||
"mypy-type-checker.args": [
|
||||
"--custom-typeshed-dir=${workspaceFolder}",
|
||||
// We only guarantee all of our tests can be run if you're on Python 3.9 or higher
|
||||
"--python-version=3.9",
|
||||
"--strict",
|
||||
// Needed because a library stubbed in typeshed won't necessarily be installed in the dev's environment
|
||||
"--ignore-missing-imports"
|
||||
],
|
||||
// Ensure typeshed's configs are used, and not user's VSCode settings
|
||||
"flake8.args": [
|
||||
"--config=.flake8"
|
||||
],
|
||||
"flake8.importStrategy": "fromEnvironment",
|
||||
"black-formatter.importStrategy": "fromEnvironment",
|
||||
// Using Ruff instead of isort
|
||||
"isort.check": false,
|
||||
"ruff.importStrategy": "fromEnvironment",
|
||||
"ruff.fixAll": true,
|
||||
"ruff.organizeImports": true,
|
||||
"evenBetterToml.formatter.alignComments": false,
|
||||
"evenBetterToml.formatter.alignEntries": false,
|
||||
"evenBetterToml.formatter.allowedBlankLines": 1,
|
||||
"evenBetterToml.formatter.arrayAutoCollapse": true,
|
||||
"evenBetterToml.formatter.arrayAutoExpand": true,
|
||||
"evenBetterToml.formatter.arrayTrailingComma": true,
|
||||
"evenBetterToml.formatter.columnWidth": 90,
|
||||
"evenBetterToml.formatter.compactArrays": true,
|
||||
"evenBetterToml.formatter.compactEntries": false,
|
||||
"evenBetterToml.formatter.compactInlineTables": false,
|
||||
"evenBetterToml.formatter.indentEntries": false,
|
||||
"evenBetterToml.formatter.indentTables": false,
|
||||
"evenBetterToml.formatter.inlineTableExpand": false,
|
||||
"evenBetterToml.formatter.reorderArrays": true,
|
||||
"evenBetterToml.formatter.trailingNewline": true,
|
||||
// We like keeping TOML keys in a certain non-alphabetical order that feels more natural
|
||||
"evenBetterToml.formatter.reorderKeys": false
|
||||
}
|
||||
+320
-418
@@ -1,240 +1,98 @@
|
||||
# Contributing to typeshed
|
||||
|
||||
Welcome! typeshed is a community project that aims to work for a wide
|
||||
range of Python users and Python codebases. If you're trying a type
|
||||
Welcome! typeshed is a community project that aims to work for a wide
|
||||
range of Python users and Python codebases. If you're trying a type
|
||||
checker on your Python code, your experience and what you can contribute
|
||||
are important to the project's success.
|
||||
|
||||
|
||||
## The contribution process at a glance
|
||||
|
||||
1. [Prepare your environment](#preparing-the-environment).
|
||||
2. Find out [where to make your changes](#where-to-make-changes).
|
||||
3. [Making your changes](#making-changes):
|
||||
1. Read the [README.md file](README.md).
|
||||
2. Set up your environment to be able to [run all tests](README.md#running-the-tests). They should pass.
|
||||
3. [Prepare your changes](#preparing-changes):
|
||||
* Small fixes and additions can be submitted directly as pull requests,
|
||||
but [contact us](README.md#discussion) before starting significant work.
|
||||
* Create your stubs, considering [what to include](#what-to-include) and
|
||||
conforming to the [coding style](https://typing.readthedocs.io/en/latest/guides/writing_stubs.html#style-guide).
|
||||
4. Optionally [format and check your stubs](#stub-content-and-style).
|
||||
5. Optionally [run the tests](tests/README.md).
|
||||
6. [Submit your changes](#submitting-changes) by opening a pull request.
|
||||
7. Make sure that all tests in CI are passing.
|
||||
but [contact us](#discussion) before starting significant work.
|
||||
* Create your stubs [conforming to the coding style](#stub-file-coding-style).
|
||||
* Make sure your tests pass cleanly on `mypy`, `pytype`, and `flake8`.
|
||||
4. [Submit your changes](#submitting-changes) by opening a pull request.
|
||||
5. You can expect a reply within a few days:
|
||||
* Diffs are merged when considered ready by the core team.
|
||||
* Feel free to ping the core team if your pull request goes without
|
||||
a reply for more than a few days.
|
||||
|
||||
You can expect a reply within a few days, but please be patient when
|
||||
it takes a bit longer. For more details, read below.
|
||||
|
||||
## Preparing the environment
|
||||
|
||||
### Code away!
|
||||
|
||||
Typeshed runs continuous integration (CI) on all pull requests. This means that
|
||||
if you file a pull request (PR), our full test suite
|
||||
-- including our linter, [`flake8-pyi`](https://github.com/pycqa/flake8-pyi) --
|
||||
is run on your PR. It also means that bots will automatically apply
|
||||
changes to your PR (using [Black](https://github.com/psf/black) and
|
||||
[Ruff](https://github.com/astral-sh/ruff)) to fix any formatting issues.
|
||||
This frees you up to ignore all local setup on your side, focus on the
|
||||
code and rely on the CI to fix everything, or point you to the places that
|
||||
need fixing.
|
||||
|
||||
### ... Or create a local development environment
|
||||
|
||||
If you prefer to run the tests and formatting locally, it's
|
||||
possible too. Follow platform-specific instructions below.
|
||||
For more information about our available tests, see
|
||||
[tests/README.md](tests/README.md).
|
||||
|
||||
Whichever platform you're using, you will need a
|
||||
virtual environment. If you're not familiar with what it is and how it works,
|
||||
please refer to this
|
||||
[documentation](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/).
|
||||
|
||||
Note that some tests require extra setup steps to install the required dependencies.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Linux / macOS / WSL</td>
|
||||
<td>
|
||||
|
||||
To install the necessary requirements, run the following commands from a
|
||||
terminal window:
|
||||
|
||||
```bash
|
||||
$ python3 -m venv .venv
|
||||
$ source .venv/bin/activate
|
||||
(.venv)$ pip install -U pip
|
||||
(.venv)$ pip install -r requirements-tests.txt
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr><!-- disables zebra striping --></tr>
|
||||
<tr>
|
||||
<td>Windows</td>
|
||||
<td>
|
||||
|
||||
Run the following commands from a Windows terminal to install all requirements:
|
||||
|
||||
```powershell
|
||||
> py -m venv .venv
|
||||
> .venv\Scripts\activate
|
||||
(.venv) > python -m pip install -U pip
|
||||
(.venv) > pip install -r requirements-tests.txt
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr><!-- disables zebra striping --></tr>
|
||||
<tr>
|
||||
<td>Using uv</td>
|
||||
<td>
|
||||
|
||||
If you already have [uv](https://docs.astral.sh/uv/getting-started/installation/) installed, you can simply replace the commands above with:
|
||||
|
||||
```shell
|
||||
uv venv
|
||||
uv pip install -r requirements-tests.txt
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Where to make changes
|
||||
|
||||
### Standard library stubs
|
||||
|
||||
The `stdlib` directory contains stubs for modules in the
|
||||
Python standard library — which
|
||||
includes pure Python modules, dynamically loaded extension modules,
|
||||
hard-linked extension modules, and the builtins. The `VERSIONS` file lists
|
||||
the versions of Python where the module is available.
|
||||
|
||||
We accept changes for future versions of Python after the first beta for that
|
||||
version was released. We drop support for a Python version three months
|
||||
after it reaches [end-of-life](https://devguide.python.org/versions/). This
|
||||
means that we will no longer actively test the stubs against that version.
|
||||
After six months, we will remove the stubs for that version and start
|
||||
to use syntax and typing features not supported by that version.
|
||||
|
||||
### Third-party library stubs
|
||||
|
||||
We accept stubs for third-party packages into typeshed as long as:
|
||||
* the package is publicly available on the [Python Package Index](https://pypi.org/);
|
||||
* the package supports any Python version supported by typeshed; and
|
||||
* the package does not ship with its own stubs or type annotations.
|
||||
|
||||
The fastest way to generate new stubs is to use `scripts/create_baseline_stubs.py` (see below).
|
||||
|
||||
Stubs for third-party packages go into the `stubs` directory. Each subdirectory
|
||||
there represents a PyPI distribution, and contains the following:
|
||||
* `METADATA.toml`, describing the package. See below for details.
|
||||
* Stubs (i.e. `*.pyi` files) for packages and modules that are shipped in the
|
||||
source distribution.
|
||||
* (Rarely) some docs specific to a given type stub package in `README` file.
|
||||
|
||||
When a third-party stub is added or
|
||||
modified, an updated version of the corresponding distribution will be
|
||||
automatically uploaded to PyPI within a few hours.
|
||||
Each time this happens the least significant
|
||||
version level is incremented. For example, if `stubs/foo/METADATA.toml` has
|
||||
`version = "x.y"` the package on PyPI will be updated from `types-foo-x.y.n`
|
||||
to `types-foo-x.y.n+1`.
|
||||
|
||||
*Note:* In its current implementation, typeshed cannot contain stubs for
|
||||
multiple versions of the same third-party library. Prefer to generate
|
||||
stubs for the latest version released on PyPI at the time of your
|
||||
stubbing.
|
||||
|
||||
#### The `METADATA.toml` file
|
||||
|
||||
The metadata file describes the stubs package using the
|
||||
[TOML file format](https://toml.io/en/). Currently, the following keys are
|
||||
supported:
|
||||
|
||||
* `version`: The versions of the library that the stubs support. Two
|
||||
formats are supported:
|
||||
- A concrete version. This is especially suited for libraries that
|
||||
use [Calendar Versioning](https://calver.org/).
|
||||
- A version range ending in `.*`. This is suited for libraries that
|
||||
reflect API changes in the version number only, where the API-independent
|
||||
part is represented by the asterisk. In the case
|
||||
of [Semantic Versioning](https://semver.org/), this version could look
|
||||
like this: `2.7.*`.
|
||||
When the stubs are updated to a newer version
|
||||
of the library, the version of the stub should be bumped (note that
|
||||
previous versions are still available on PyPI).
|
||||
* `dependencies` (optional): A list of other stub packages or packages with type
|
||||
information that are imported by the stubs in this package. Only packages
|
||||
generated by typeshed or required by the upstream package are allowed to
|
||||
be listed here, for security reasons. See
|
||||
[this issue](https://github.com/typeshed-internal/stub_uploader/issues/90)
|
||||
for more information about what external dependencies are allowed.
|
||||
* `extra-description` (optional): Can be used to add a custom description to
|
||||
the package's long description. It should be a multi-line string in
|
||||
Markdown format.
|
||||
* `stub-distribution` (optional): Distribution name to be uploaded to PyPI.
|
||||
This defaults to `types-<distribution>` and should only be set in special
|
||||
cases.
|
||||
* `upstream-repository` (recommended): The URL of the upstream repository.
|
||||
* `obsolete-since` (optional): This field is part of our process for
|
||||
[removing obsolete third-party libraries](#third-party-library-removal-policy).
|
||||
It contains the first version of the corresponding library that ships
|
||||
its own `py.typed` file.
|
||||
* `no-longer-updated` (optional): This field is set to `true` before removing
|
||||
stubs for other reasons than the upstream library shipping with type
|
||||
information.
|
||||
* `upload` (optional): This field is set to `false` to prevent automatic
|
||||
uploads to PyPI. This should only be used in special cases, e.g. when the stubs
|
||||
break the upload.
|
||||
* `partial-stub` (optional): This field marks the type stub package as
|
||||
[partial](https://peps.python.org/pep-0561/#partial-stub-packages). This is for
|
||||
3rd-party stubs that don't cover the entirety of the package's public API.
|
||||
* `requires-python` (optional): The minimum version of Python required to install
|
||||
the type stub package. It must be in the form `>=3.*`. If omitted, the oldest
|
||||
Python version supported by typeshed is used.
|
||||
|
||||
In addition, we specify configuration for stubtest in the `tool.stubtest` table.
|
||||
This has the following keys:
|
||||
* `skip` (default: `false`): Whether stubtest should be run against this
|
||||
package. Please avoid setting this to `true`, and add a comment if you have
|
||||
to.
|
||||
* `ignore-missing-stub`: When set to `true`, this will add the
|
||||
`--ignore-missing-stub` option to the stubtest call. See
|
||||
[tests/README.md](./tests/README.md) for more information. In most cases,
|
||||
this field should be identical to `partial-stub`.
|
||||
* `stubtest-dependencies` (default: `[]`): A list of Python packages that need
|
||||
to be installed for stubtest to run successfully. These packages are installed
|
||||
in addition to the dependencies in the `dependencies` field.
|
||||
* `apt-dependencies` (default: `[]`): A list of Ubuntu APT packages
|
||||
that need to be installed for stubtest to run successfully.
|
||||
* `brew-dependencies` (default: `[]`): A list of MacOS Homebrew packages
|
||||
that need to be installed for stubtest to run successfully
|
||||
* `choco-dependencies` (default: `[]`): A list of Windows Chocolatey packages
|
||||
that need to be installed for stubtest to run successfully
|
||||
* `supported-platforms` (default: all platforms): A list of OSes on which
|
||||
stubtest can be run. When a package is not platform-specific, this should
|
||||
not be set. If the package is platform-specific, this should usually be set
|
||||
to the supported platforms, unless stubtest is known to fail on a
|
||||
specific platform.
|
||||
* `ci-platforms` (default: `["linux"]`): A list of OSes on which to run
|
||||
stubtest as part of our continuous integration (CI) tests. Can contain
|
||||
`win32`, `linux`, and `darwin` values. If not specified, stubtest is run
|
||||
only on `linux`. Only add extra OSes to the test if there are
|
||||
platform-specific branches in a stubs package.
|
||||
* `mypy-plugins` (default: `[]`): A list of Python modules to use as mypy plugins
|
||||
when running stubtest. For example: `mypy-plugins = ["mypy_django_plugin.main"]`
|
||||
* `mypy-plugins-config` (default: `{}`): A dictionary mapping plugin names to their
|
||||
configuration dictionaries for use by mypy plugins. For example:
|
||||
`mypy-plugins-config = {"django-stubs" = {"django_settings_module" = "@tests.django_settings"}}`
|
||||
|
||||
`*-dependencies` are usually packages needed to `pip install` the implementation
|
||||
distribution.
|
||||
|
||||
The format of all `METADATA.toml` files can be checked by running
|
||||
`python3 ./tests/check_typeshed_structure.py`.
|
||||
For more details, read below.
|
||||
|
||||
|
||||
## Making Changes
|
||||
## 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.
|
||||
|
||||
For less formal discussion, try the typing chat room on
|
||||
[gitter.im](https://gitter.im/python/typing). Some Mypy core developers
|
||||
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.
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
Everyone participating in the typeshed community, and in particular in
|
||||
our issue tracker, pull requests, and IRC channel, is expected to treat
|
||||
other people with respect and more generally to follow the guidelines
|
||||
articulated in the [Python Community Code of
|
||||
Conduct](https://www.python.org/psf/codeofconduct/).
|
||||
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
Even more excellent than a good bug report is a fix for a bug, or the
|
||||
implementation of a much-needed stub. We'd love to have
|
||||
your contributions.
|
||||
|
||||
We use the usual GitHub pull-request flow, which may be familiar to
|
||||
you if you've contributed to other projects on GitHub. For the
|
||||
mechanics, see [Mypy's git and GitHub workflow help page](https://github.com/python/mypy/wiki/Using-Git-And-GitHub),
|
||||
or [GitHub's own documentation](https://help.github.com/articles/using-pull-requests/).
|
||||
|
||||
Anyone interested in type stubs may review your code. One of the core
|
||||
developers will merge your pull request when they think it's ready.
|
||||
For every pull request, we aim to promptly either merge it or say why
|
||||
it's not yet ready; if you go a few days without a reply, please feel
|
||||
free to ping the thread by adding a new comment.
|
||||
|
||||
To get your pull request merged sooner, you should explain why you are
|
||||
making the change. For example, you can point to a code sample that is
|
||||
processed incorrectly by a type checker. It is also helpful to add
|
||||
links to online documentation or to the implementation of the code
|
||||
you are changing.
|
||||
|
||||
Also, do not squash your commits after you have submitted a pull request, as this
|
||||
erases context during review. We will squash commits when the pull request is merged.
|
||||
|
||||
At present the core developers are (alphabetically):
|
||||
* David Fisher (@ddfisher)
|
||||
* Łukasz Langa (@ambv)
|
||||
* Jukka Lehtosalo (@JukkaL)
|
||||
* Ivan Levkivskyi (@ilevkivskyi)
|
||||
* Matthias Kramm (@matthiaskramm)
|
||||
* Greg Price (@gnprice)
|
||||
* Sebastian Rittau (@srittau)
|
||||
* Guido van Rossum (@gvanrossum)
|
||||
* Jelle Zijlstra (@JelleZijlstra)
|
||||
|
||||
NOTE: the process for preparing and submitting changes also applies to
|
||||
core developers. This ensures high quality contributions and keeps
|
||||
everybody on the same page. Avoid direct pushes to the repository.
|
||||
|
||||
|
||||
## Preparing Changes
|
||||
|
||||
### Before you begin
|
||||
|
||||
@@ -243,33 +101,6 @@ recommend starting by opening an issue laying out what you want to do.
|
||||
That lets a conversation happen early in case other contributors disagree
|
||||
with what you'd like to do or have ideas that will help you do it.
|
||||
|
||||
### Stub Content and Style
|
||||
|
||||
Each Python module is represented by a .pyi "stub file". This is a syntactically valid Python file, where all methods are empty and [type annotations](https://typing.readthedocs.io/en/latest/spec/annotations.html) are used to describe function signatures and variable types.
|
||||
|
||||
Typeshed follows the standard type system guidelines for [stub content](https://typing.readthedocs.io/en/latest/guides/writing_stubs.html#stub-content) and [coding style](https://typing.readthedocs.io/en/latest/guides/writing_stubs.html#style-guide).
|
||||
|
||||
The code is formatted using [`Black`](https://github.com/psf/black).
|
||||
Various other autofixes and lint rules are
|
||||
also performed by [`Ruff`](https://github.com/astral-sh/ruff) and
|
||||
[`Flake8`](https://github.com/pycqa/flake8),
|
||||
with plugin [`flake8-pyi`](https://github.com/pycqa/flake8-pyi).
|
||||
|
||||
The repository is equipped with a [pre-commit.ci](https://pre-commit.ci/)
|
||||
configuration file. This means that you don't *need* to do anything yourself to
|
||||
run the code formatters or linters. When you push a commit, a bot will run
|
||||
those for you right away and add any autofixes to your PR. Anything
|
||||
that can't be autofixed will show up as a CI failure, hopefully with an error
|
||||
message that will make it clear what's gone wrong.
|
||||
|
||||
That being said, if you *want* to run the formatters and linters locally
|
||||
when you commit, you're free to do so. To use the same configuration as we use
|
||||
in CI, we recommend doing this via pre-commit:
|
||||
|
||||
```bash
|
||||
(.venv)$ pre-commit run --all-files
|
||||
```
|
||||
|
||||
### What to include
|
||||
|
||||
Stubs should include the complete interface (classes, functions,
|
||||
@@ -285,189 +116,260 @@ or if they are not prefixed with an underscore. This means
|
||||
that typeshed will generally accept contributions that add missing
|
||||
objects, even if they are undocumented. Undocumented objects should
|
||||
be marked with a comment of the form ``# undocumented``.
|
||||
|
||||
### Incomplete Annotations
|
||||
|
||||
When submitting new stubs, it is not necessary to annotate all arguments,
|
||||
return types, and fields. Such items should either be left unannotated or
|
||||
use `_typeshed.Incomplete` if this is not possible:
|
||||
Example:
|
||||
|
||||
```python
|
||||
from _typeshed import Incomplete
|
||||
|
||||
field: Incomplete # unannotated
|
||||
|
||||
def foo(x): ... # unannotated argument and return type
|
||||
def list2cmdline(seq: Sequence[str]) -> str: ... # undocumented
|
||||
```
|
||||
|
||||
`Incomplete` can also be used for partially known types:
|
||||
We accept such undocumented objects because omitting objects can confuse
|
||||
users. Users who see an error like "module X has no attribute Y" will
|
||||
not know whether the error appeared because their code had a bug or
|
||||
because the stub is wrong. Although it may also be helpful for a type
|
||||
checker to point out usage of private objects, we usually prefer false
|
||||
negatives (no errors for wrong code) over false positives (type errors
|
||||
for correct code). In addition, even for private objects a type checker
|
||||
can be helpful in pointing out that an incorrect type was used.
|
||||
|
||||
### Incomplete stubs
|
||||
|
||||
We accept partial stubs, especially for larger packages. These need to
|
||||
follow the following guidelines:
|
||||
|
||||
* Included functions and methods must list all arguments, but the arguments
|
||||
can be left unannotated. Do not use `Any` to mark unannotated arguments
|
||||
or return values.
|
||||
* Partial classes must include a `__getattr__()` method marked with an
|
||||
`# incomplete` comment (see example below).
|
||||
* Partial modules (i.e. modules that are missing some or all classes,
|
||||
functions, or attributes) must include a top-level `__getattr__()`
|
||||
function marked with an `# incomplete` comment (see example below).
|
||||
* Partial packages (i.e. packages that are missing one or more sub-modules)
|
||||
must have a `__init__.pyi` stub that is marked as incomplete (see above).
|
||||
A better alternative is to create empty stubs for all sub-modules and
|
||||
mark them as incomplete individually.
|
||||
|
||||
Example of a partial module with a partial class `Foo` and a partially
|
||||
annotated function `bar()`:
|
||||
|
||||
```python
|
||||
def foo(x: Incomplete | None) -> list[Incomplete]: ...
|
||||
def __getattr__(name: str) -> Any: ... # incomplete
|
||||
|
||||
class Foo:
|
||||
def __getattr__(self, name: str) -> Any: # incomplete
|
||||
x: int
|
||||
y: str
|
||||
|
||||
def bar(x: str, y, *, z=...): ...
|
||||
```
|
||||
|
||||
### Using stubgen
|
||||
|
||||
Mypy includes a tool called [stubgen](https://mypy.readthedocs.io/en/latest/stubgen.html)
|
||||
that auto-generates stubs for Python and C modules using static analysis,
|
||||
Sphinx docs, and runtime introspection. It can be used to get a starting
|
||||
point for your stubs. Note that this generator is currently unable to
|
||||
determine most argument and return types and omits them or uses ``Any`` in
|
||||
their place. Fill out manually the types that you know.
|
||||
|
||||
### Stub file coding style
|
||||
|
||||
#### Syntax example
|
||||
|
||||
The below is an excerpt from the types for the `datetime` module.
|
||||
|
||||
```python
|
||||
MAXYEAR: int
|
||||
MINYEAR: int
|
||||
|
||||
class date:
|
||||
def __init__(self, year: int, month: int, day: int) -> None: ...
|
||||
@classmethod
|
||||
def fromtimestamp(cls, timestamp: float) -> date: ...
|
||||
@classmethod
|
||||
def today(cls) -> date: ...
|
||||
@classmethod
|
||||
def fromordinal(cls, ordinal: int) -> date: ...
|
||||
@property
|
||||
def year(self) -> int: ...
|
||||
def replace(self, year: int = ..., month: int = ..., day: int = ...) -> date: ...
|
||||
def ctime(self) -> str: ...
|
||||
def weekday(self) -> int: ...
|
||||
```
|
||||
|
||||
#### Conventions
|
||||
|
||||
Stub files are *like* Python files and you should generally expect them
|
||||
to look the same. Your tools should be able to successfully treat them
|
||||
as regular Python files. However, there are a few important differences
|
||||
you should know about.
|
||||
|
||||
Style conventions for stub files are different from PEP 8. The general
|
||||
rule is that they should be as concise as possible. Specifically:
|
||||
* lines can be up to 130 characters long;
|
||||
* functions and methods that don't fit in one line should be split up
|
||||
with one argument per line;
|
||||
* all function bodies should be empty;
|
||||
* prefer ``...`` over ``pass``;
|
||||
* prefer ``...`` on the same line as the class/function signature;
|
||||
* avoid vertical whitespace between consecutive module-level functions,
|
||||
names, or methods and fields within a single class;
|
||||
* use a single blank line between top-level class definitions, or none
|
||||
if the classes are very small;
|
||||
* do not use docstrings;
|
||||
* use variable annotations instead of type comments, even for stubs
|
||||
that target older versions of Python;
|
||||
* for arguments with a type and a default, use spaces around the `=`.
|
||||
The code formatter [black](https://github.com/psf/black) will format
|
||||
stubs according to this standard.
|
||||
|
||||
Stub files should only contain information necessary for the type
|
||||
checker, and leave out unnecessary detail:
|
||||
* for arguments with a default, use `...` instead of the actual
|
||||
default;
|
||||
* for arguments that default to `None`, use `Optional[]` explicitly
|
||||
(see below for details);
|
||||
* use `float` instead of `Union[int, float]`.
|
||||
|
||||
Some further tips for good type hints:
|
||||
* avoid invariant collection types (`List`, `Dict`) in argument
|
||||
positions, in favor of covariant types like `Mapping` or `Sequence`;
|
||||
* avoid Union return types: https://github.com/python/mypy/issues/1693;
|
||||
* in Python 2, whenever possible, use `unicode` if that's the only
|
||||
possible type, and `Text` if it can be either `unicode` or `bytes`;
|
||||
* use platform checks like `if sys.platform == 'win32'` to denote
|
||||
platform-dependent APIs.
|
||||
|
||||
Imports in stubs are considered private (not part of the exported API)
|
||||
unless:
|
||||
* they use the form ``from library import name as name`` (sic, using
|
||||
explicit ``as`` even if the name stays the same); or
|
||||
* they use the form ``from library import *`` which means all names
|
||||
from that library are exported.
|
||||
|
||||
When adding type hints, avoid using the `Any` type when possible. Reserve
|
||||
the use of `Any` for when:
|
||||
* the correct type cannot be expressed in the current type system; and
|
||||
* to avoid Union returns (see above).
|
||||
|
||||
Note that `Any` is not the correct type to use if you want to indicate
|
||||
that some function can accept literally anything: in those cases use
|
||||
`object` instead.
|
||||
|
||||
For arguments with type and a default value of `None`, PEP 484
|
||||
prescribes that the type automatically becomes `Optional`. However we
|
||||
prefer explicit over implicit in this case, and require the explicit
|
||||
`Optional[]` around the type. The mypy tests enforce this (through
|
||||
the use of --no-implicit-optional) and the error looks like
|
||||
`Incompatible types in assignment (expression has type None, variable
|
||||
has type "Blah") `.
|
||||
|
||||
Stub files support forward references natively. In other words, the
|
||||
order of class declarations and type aliases does not matter in
|
||||
a stub file. You can also use the name of the class within its own
|
||||
body. Focus on making your stubs clear to the reader. Avoid using
|
||||
string literals in type annotations.
|
||||
|
||||
Type variables and aliases you introduce purely for legibility reasons
|
||||
should be prefixed with an underscore to make it obvious to the reader
|
||||
they are not part of the stubbed API.
|
||||
|
||||
When adding type annotations for context manager classes, annotate
|
||||
the return type of `__exit__` as bool only if the context manager
|
||||
sometimes suppresses annotations -- if it sometimes returns `True`
|
||||
at runtime. If the context manager never suppresses exceptions,
|
||||
have the return type be either `None` or `Optional[bool]`. If you
|
||||
are not sure whether exceptions are suppressed or not or if the
|
||||
context manager is meant to be subclassed, pick `Optional[bool]`.
|
||||
See https://github.com/python/mypy/issues/7214 for more details.
|
||||
|
||||
NOTE: there are stubs in this repository that don't conform to the
|
||||
style described above. Fixing them is a great starting point for new
|
||||
contributors.
|
||||
|
||||
### Stub versioning
|
||||
|
||||
There are separate directories for `stdlib` and `third_party` stubs.
|
||||
Within those, there are separate directories for different versions of
|
||||
Python the stubs target.
|
||||
|
||||
The directory name indicates the major version of Python that a stub targets
|
||||
and optionally the lowest minor version, with the exception of the `2and3`
|
||||
directory which applies to both Python 2 and 3.
|
||||
|
||||
For example, stubs in the `3` directory will be applied to all versions of
|
||||
Python 3, though stubs in the `3.7` directory will only be applied to versions
|
||||
3.7 and above. However, stubs in the `2` directory will not be applied to
|
||||
Python 3.
|
||||
|
||||
It is preferred to use a single stub in the more generic directory that
|
||||
conditionally targets specific versions when needed, as opposed
|
||||
to maintaining multiple stub files within more specific directories. Similarly,
|
||||
if the given library works on both Python 2 and Python 3, prefer to put your
|
||||
stubs in the `2and3` directory, unless the types are so different that the stubs
|
||||
become unreadable that way.
|
||||
|
||||
You can use checks like `if sys.version_info >= (3, 8):` to denote new
|
||||
functionality introduced in a given Python version or solve type
|
||||
differences. When doing so, only use one-tuples or two-tuples. This is
|
||||
because:
|
||||
|
||||
* mypy doesn't support more fine-grained version checks; and more
|
||||
importantly
|
||||
|
||||
* the micro versions of a Python release will change over time in your
|
||||
checking environment and the checker should return consistent results
|
||||
regardless of the micro version used.
|
||||
|
||||
Because of this, if a given functionality was introduced in, say, Python
|
||||
3.7.4, your check:
|
||||
|
||||
* should be expressed as `if sys.version_info >= (3, 7):`
|
||||
* should NOT be expressed as `if sys.version_info >= (3, 7, 4):`
|
||||
* should NOT be expressed as `if sys.version_info >= (3, 8):`
|
||||
|
||||
This makes the type checker assume the functionality was also available
|
||||
in 3.7.0 - 3.7.3, which while *technically* incorrect is relatively
|
||||
harmless. This is a strictly better compromise than using the latter
|
||||
two forms, which would generate false positive errors for correct use
|
||||
under Python 3.7.4.
|
||||
|
||||
Note: in its current implementation, typeshed cannot contain stubs for
|
||||
multiple versions of the same third-party library. Prefer to generate
|
||||
stubs for the latest version released on PyPI at the time of your
|
||||
stubbing.
|
||||
|
||||
### What to do when a project's documentation and implementation disagree
|
||||
|
||||
Type stubs are meant to be external type annotations for a given
|
||||
library. While they are useful documentation in their own right, they
|
||||
library. While they are useful documentation in its own merit, they
|
||||
augment the project's concrete implementation, not the project's
|
||||
documentation. Whenever you find them disagreeing, model the type
|
||||
information after the actual implementation and file an issue on the
|
||||
project's tracker to fix their documentation.
|
||||
|
||||
### Docstrings
|
||||
## Issue-tracker conventions
|
||||
|
||||
Typeshed stubs should not include duplicated docstrings from the source code.
|
||||
We aim to reply to all new issues promptly. We'll assign one or more
|
||||
labels to indicate we've triaged an issue, but most typeshed issues
|
||||
are relatively simple (stubs for a given module or package are
|
||||
missing, incomplete or incorrect) and we won't add noise to the
|
||||
tracker by labeling all of them. Please see the
|
||||
[list of all labels](https://github.com/python/typeshed/issues/labels)
|
||||
for a detailed description of the labels we use.
|
||||
|
||||
### Auto-generating stub files
|
||||
Sometimes a PR can't make progress until some external issue is
|
||||
addressed. We indicate this by editing the subject to add a ``[WIP]``
|
||||
prefix. (This should be removed before committing the issue once
|
||||
unblocked!)
|
||||
|
||||
Typeshed includes `scripts/create_baseline_stubs.py`.
|
||||
It generates stubs automatically using a tool called
|
||||
[stubgen](https://mypy.readthedocs.io/en/latest/stubgen.html) that comes with mypy.
|
||||
### Core developer guidelines
|
||||
|
||||
To get started, fork typeshed, clone your fork, and then
|
||||
[create a virtualenv](#-or-create-a-local-development-environment).
|
||||
You can then install the library with `pip` into the virtualenv and run the script below,
|
||||
replacing `$INSERT_LIBRARY_NAME_HERE` with the name of the library:
|
||||
Core developers should follow these rules when processing pull requests:
|
||||
|
||||
```bash
|
||||
(.venv)$ pip install $INSERT_LIBRARY_NAME_HERE
|
||||
(.venv)$ python3 scripts/create_baseline_stubs.py $INSERT_LIBRARY_NAME_HERE
|
||||
```
|
||||
|
||||
When the script has finished running, it will print instructions telling you what to do next.
|
||||
|
||||
If it has been a while since you set up the virtualenv, make sure you have
|
||||
the latest mypy (`pip install -r requirements-tests.txt`) before running the script.
|
||||
|
||||
### Supported type system features
|
||||
|
||||
Since [PEP 484](https://peps.python.org/pep-0484/) was accepted, there have been
|
||||
many other PEPs that added new features to the Python type system. In general,
|
||||
new features can be used in typeshed as soon as the PEP has been accepted and
|
||||
implemented and most type checkers support the new feature.
|
||||
|
||||
Supported features include:
|
||||
- [PEP 544](https://peps.python.org/pep-0544/) (`Protocol`)
|
||||
- [PEP 585](https://peps.python.org/pep-0585/) (builtin generics)
|
||||
- [PEP 586](https://peps.python.org/pep-0586/) (`Literal`)
|
||||
- [PEP 591](https://peps.python.org/pep-0591/) (`Final`/`@final`)
|
||||
- [PEP 589](https://peps.python.org/pep-0589/) (`TypedDict`)
|
||||
- [PEP 604](https://peps.python.org/pep-0604/) (`Foo | Bar` union syntax)
|
||||
- [PEP 612](https://peps.python.org/pep-0612/) (`ParamSpec`)
|
||||
- [PEP 647](https://peps.python.org/pep-0647/) (`TypeGuard`):
|
||||
see [#5406](https://github.com/python/typeshed/issues/5406)
|
||||
- [PEP 655](https://peps.python.org/pep-0655/) (`Required` and `NotRequired`)
|
||||
- [PEP 673](https://peps.python.org/pep-0673/) (`Self`)
|
||||
- [PEP 675](https://peps.python.org/pep-0675/) (`LiteralString`)
|
||||
- [PEP 702](https://peps.python.org/pep-0702/) (`@deprecated()`)
|
||||
|
||||
Features from the `typing` module that are not present in all
|
||||
supported Python versions must be imported from `typing_extensions`
|
||||
instead in typeshed stubs. This currently affects:
|
||||
|
||||
- `TypeAlias` (new in Python 3.10)
|
||||
- `Concatenate` (new in Python 3.10)
|
||||
- `ParamSpec` (new in Python 3.10)
|
||||
- `TypeGuard` (new in Python 3.10)
|
||||
- `Self` (new in Python 3.11)
|
||||
- `Never` (new in Python 3.11)
|
||||
- `LiteralString` (new in Python 3.11)
|
||||
- `TypeVarTuple` and `Unpack` (new in Python 3.11)
|
||||
- `Required` and `NotRequired` (new in Python 3.11)
|
||||
- `Buffer` (new in Python 3.12; in the `collections.abc` module)
|
||||
- `@deprecated` (new in Python 3.13; in the `warnings` module)
|
||||
|
||||
Some type checkers implicitly promote the `bytearray` and
|
||||
`memoryview` types to `bytes`.
|
||||
[PEP 688](https://www.python.org/dev/peps/pep-0688/) removes
|
||||
this implicit promotion.
|
||||
Typeshed stubs should be written assuming that these promotions
|
||||
do not happen, so a parameter that accepts either `bytes` or
|
||||
`bytearray` should be typed as `bytes | bytearray`.
|
||||
Often one of the aliases from `_typeshed`, such as
|
||||
`_typeshed.ReadableBuffer`, can be used instead.
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
Even more excellent than a good bug report is a fix for a bug, or the
|
||||
implementation of a much-needed stub. We'd love to have
|
||||
your contributions.
|
||||
|
||||
We use the usual GitHub pull-request flow, which may be familiar to
|
||||
you if you've contributed to other projects on GitHub. For the
|
||||
mechanics, see [Mypy's git and GitHub workflow help page](https://github.com/python/mypy/wiki/Using-Git-And-GitHub),
|
||||
or [GitHub's own documentation](https://help.github.com/articles/using-pull-requests/).
|
||||
|
||||
Anyone interested in type stubs may review your code. One of the
|
||||
maintainers will merge your pull request when they think it's ready.
|
||||
For every pull request, we aim to promptly either merge it or say why
|
||||
it's not yet ready; if you go a few days without a reply, please feel
|
||||
free to ping the thread by adding a new comment.
|
||||
|
||||
To get your pull request merged sooner, you should explain why you are
|
||||
making the change. For example, you can point to a code sample that is
|
||||
processed incorrectly by a type checker. It is also helpful to add
|
||||
links to online documentation or to the implementation of the code
|
||||
you are changing.
|
||||
|
||||
As the author of the pull request, it is your responsibility to make
|
||||
sure all CI tests pass and that any feedback is addressed. The typeshed
|
||||
maintainers will probably provide some help and may even push changes
|
||||
to your PR to fix any minor issues, but this is not always possible.
|
||||
If a PR lingers with unresolved problems for too long, we may close it
|
||||
([see below](#closing-stale-prs)).
|
||||
|
||||
Also, do not squash your commits or use `git commit --amend` after you have submitted a pull request, as this
|
||||
erases context during review. We will squash commits when the pull request is merged.
|
||||
This way, your pull request will appear as a single commit in our git history, even
|
||||
if it consisted of several smaller commits.
|
||||
|
||||
## Third-party library removal policy
|
||||
|
||||
Third-party stubs are generally removed from typeshed when one of the
|
||||
following criteria is met:
|
||||
|
||||
* The upstream package ships a `py.typed` file for at least six months,
|
||||
and the upstream type annotations are of a comparable standard to those in
|
||||
typeshed, or
|
||||
* the upstream package was declared or appears to be unmaintained, and
|
||||
retaining the stubs causes maintenance issues in typeshed.
|
||||
|
||||
Case 1: If a package ships its own `py.typed` file, please follow these steps:
|
||||
|
||||
1. Make sure **stubsabot** open a PR that sets the `obsolete-since` field in the
|
||||
`METADATA.toml` file to the first version of the package that shipped `py.typed`.
|
||||
2. After at least six months, make sure **stubsabot** open a PR to remove the stubs.
|
||||
|
||||
Case 2: If third-party stubs should be removed for other reasons, please follow
|
||||
these steps:
|
||||
|
||||
1. Open an issue explaining why the stubs should be removed.
|
||||
2. A maintainer will add the
|
||||
["stubs: removal" label](https://github.com/python/typeshed/labels/%22stubs%3A%20removal%22).
|
||||
3. Open a PR that sets the `no-longer-updated` field in the `METADATA.toml`
|
||||
file to `true`.
|
||||
4. When a new version of the package was automatically uploaded to PyPI (which
|
||||
can take up to a day), make sure **stubsabot** open a PR to remove the stubs.
|
||||
|
||||
If feeling kindly, please update [mypy](https://github.com/python/mypy/blob/master/mypy/stubinfo.py)
|
||||
for any stub obsoletions or removals.
|
||||
|
||||
### Marking PRs as "deferred"
|
||||
|
||||
We sometimes use the ["status: deferred" label](https://github.com/python/typeshed/labels/%22status%3A%20deferred%22)
|
||||
to mark PRs and issues that we'd like to accept, but that are blocked by some
|
||||
external factor. Blockers can include:
|
||||
|
||||
- An unambiguous bug in a type checker (i.e., a case where the
|
||||
type checker is not implementing [the typing spec](https://typing.readthedocs.io/en/latest/spec/index.html)).
|
||||
- A dependency on a typing PEP that is still under consideration.
|
||||
- A pending change in a related project, such as stub-uploader.
|
||||
|
||||
### Closing stale PRs
|
||||
|
||||
To keep the number of open PRs manageable, we may close PRs when they have been
|
||||
open for too long. Specifically, we close open PRs that either have failures in CI,
|
||||
serious merge conflicts or unaddressed feedback, and that have not seen any
|
||||
activity in three months.
|
||||
* Always wait for tests to pass before merging PRs.
|
||||
* Use "[Squash and merge](https://github.com/blog/2141-squash-your-commits)" to merge PRs.
|
||||
* Delete branches for merged PRs (by core devs pushing to the main repo).
|
||||
* Make sure commit messages to master are meaningful. For example, remove irrelevant
|
||||
intermediate commit messages.
|
||||
* If stubs for a new library are submitted, notify the library's maintainers.
|
||||
|
||||
@@ -235,3 +235,4 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
= = = = =
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
At present the active maintainers are (alphabetically):
|
||||
|
||||
* Rebecca Chen (@rchen152)
|
||||
* Jukka Lehtosalo (@JukkaL)
|
||||
* Ivan Levkivskyi (@ilevkivskyi)
|
||||
* Sebastian Rittau (@srittau)
|
||||
* Guido van Rossum (@gvanrossum)
|
||||
* Brian Schubert (@brianschubert)
|
||||
* Shantanu (@hauntsaninja)
|
||||
* Nikita Sobolev (@sobolevn)
|
||||
* Samuel Therrien (@Avasam)
|
||||
* Aku Viljanen (@Akuli)
|
||||
* Alex Waygood (@AlexWaygood)
|
||||
* Jelle Zijlstra (@JelleZijlstra)
|
||||
|
||||
Former maintainers include:
|
||||
|
||||
* David Fisher (@ddfisher)
|
||||
* Matthias Kramm (@matthiaskramm)
|
||||
* Łukasz Langa (@ambv)
|
||||
* Greg Price (@gnprice)
|
||||
* Rune Tynan (@CraftSpider)
|
||||
|
||||
For security reasons, maintainers who haven't been active for twelve months
|
||||
(no PR reviews or merges, no opened PRs, no significant participation in
|
||||
issues or typing-related discussion) will have their access rights removed.
|
||||
They will also be moved to the "former maintainers" section here.
|
||||
|
||||
Former maintainers who want their access rights restored should open
|
||||
an issue or mail one of the active maintainers.
|
||||
|
||||
## Maintainer guidelines
|
||||
|
||||
The process for preparing and submitting changes as outlined
|
||||
in the [CONTRIBUTING document](./CONTRIBUTING.md) also applies to
|
||||
maintainers. This ensures high quality contributions and keeps
|
||||
everybody on the same page. Do not make direct pushes to the repository.
|
||||
|
||||
### Reviewing and merging pull requests
|
||||
|
||||
When reviewing pull requests, follow these guidelines:
|
||||
|
||||
* Typing is hard. Try to be helpful and explain issues with the PR,
|
||||
especially to new contributors.
|
||||
* When reviewing auto-generated stubs, just scan for red flags and obvious
|
||||
errors. Leave possible manual improvements for separate PRs.
|
||||
* When reviewing large, hand-crafted PRs, you only need to look for red flags
|
||||
and general issues, and do a few spot checks.
|
||||
* Review smaller, hand-crafted PRs thoroughly.
|
||||
|
||||
When merging pull requests, follow these guidelines:
|
||||
|
||||
* Always wait for tests to pass before merging PRs.
|
||||
* Use "[Squash and merge](https://github.com/blog/2141-squash-your-commits)" to merge PRs.
|
||||
* Make sure the commit message is meaningful. For example, remove irrelevant
|
||||
intermediate commit messages.
|
||||
* The commit message for third-party stubs is used to generate the changelog.
|
||||
It should be valid Markdown, be comprehensive, read like a changelog entry,
|
||||
and assume that the reader has no access to the diff.
|
||||
* Delete branches for merged PRs (by maintainers pushing to the main repo).
|
||||
|
||||
### Marking PRs as "deferred"
|
||||
|
||||
*See also the [guidelines in the CONTRIBUTING file](./CONTRIBUTING.md#marking-prs-as-deferred).*
|
||||
|
||||
PRs should only be marked as "deferred" if there is a clear path towards getting
|
||||
the blocking issue resolved within a reasonable time frame. If a PR depends on
|
||||
a more amorphous change, such as a type system change that has not yet reached
|
||||
the PEP stage, it should instead be closed.
|
||||
|
||||
Maintainers who add the "deferred" label should state clearly what exactly the
|
||||
blocker is, usually with a link to an open issue in another project.
|
||||
|
||||
### Closing stale PRs
|
||||
|
||||
*See also the [guidelines in the CONTRIBUTING file](./CONTRIBUTING.md#closing-stale-prs).*
|
||||
|
||||
We want to maintain a welcoming atmosphere for contributors, so use a friendly
|
||||
message when closing the PR. Example message:
|
||||
|
||||
Thanks for contributing! I'm closing this PR for now, because it still <fails some tests OR has unresolved review feedback OR has a merge conflict> after three months of inactivity. If you are still interested, please feel free to open a new PR (or ping us to reopen this one).
|
||||
|
||||
### Closing PRs for future standard library changes
|
||||
|
||||
*See also the [guidelines in the CONTRIBUTING file](./CONTRIBUTING.md#standard-library-stubs).*
|
||||
|
||||
When rejecting a PR for a change for a future Python version, use a message
|
||||
like:
|
||||
|
||||
Thanks for contributing! Unfortunately, [as outlined in our CONTRIBUTING document](https://github.com/python/typeshed/blob/main/CONTRIBUTING.md#standard-library-stubs) we only accept pull requests to the standard library for future Python versions after the first beta version has been released. This is in part to prevent churn in the stubs, and in part because the testing infrastructure for the future version is not yet in place. Please feel free to open a new PR when the first beta version has been released. Alternatively, if this PR is still relevant, you can leave a comment here to reopen it.
|
||||
|
||||
### Closing requests for third-party stubs
|
||||
|
||||
We don't keep requests for third-party library stubs open. Close those
|
||||
requests as "not planned" with an explanation like this:
|
||||
|
||||
We gladly accept type stub contributions for third-party libraries that are published on PyPI in typeshed. To contribute a new library, please follow the steps outlined in [CONTRIBUTING.md](/python/typeshed/blob/main/CONTRIBUTING.md). The `create_baseline_stubs.py` script can be useful to create an initial version, suitable for inclusion in typeshed.
|
||||
|
||||
That said, we don't keep requests for third-party library stubs open, unless there are issues that need to be addressed before a PR can be opened. Therefore, I'm closing this issue.
|
||||
@@ -1,124 +1,150 @@
|
||||
# typeshed
|
||||
|
||||
[](https://github.com/python/typeshed/actions/workflows/tests.yml)
|
||||
[](https://travis-ci.org/python/typeshed)
|
||||
[](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)
|
||||
[](https://github.com/python/typeshed/blob/master/CONTRIBUTING.md)
|
||||
|
||||
## About
|
||||
|
||||
Typeshed contains external type annotations for the Python standard library
|
||||
and Python builtins, as well as third-party packages that are contributed by
|
||||
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.
|
||||
This data can e.g. be used for static analysis, type checking or type inference.
|
||||
|
||||
For information on how to use typeshed, read below. Information for
|
||||
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.10 to 3.14.
|
||||
Typeshed supports Python versions 2.7 and 3.5 and up.
|
||||
|
||||
## Using
|
||||
|
||||
If you're just using a type checker (e.g. [mypy](https://github.com/python/mypy/),
|
||||
[pyright](https://github.com/microsoft/pyright), or PyCharm's built-in type
|
||||
checker), as opposed to
|
||||
If you're just using mypy (or pytype or PyCharm), as opposed to
|
||||
developing it, you don't need to interact with the typeshed repo at
|
||||
all: a copy of the 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
|
||||
all: a copy of typeshed is bundled with mypy.
|
||||
|
||||
```bash
|
||||
$ pip install types-html5lib types-requests
|
||||
When you use a checked-out clone of the mypy repo, a copy of typeshed
|
||||
should be included as a submodule, using
|
||||
|
||||
$ git clone --recurse-submodules https://github.com/python/mypy.git
|
||||
|
||||
or
|
||||
|
||||
$ git clone https://github.com/python/mypy.git
|
||||
$ cd mypy
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
|
||||
and occasionally you will have to repeat the final command (`git
|
||||
submodule update`) to pull in changes made in the upstream typeshed
|
||||
repo.
|
||||
|
||||
PyCharm and pytype similarly include a copy of typeshed. The one in
|
||||
pytype can be updated in the same way if you are working with the
|
||||
pytype repo.
|
||||
|
||||
## Format
|
||||
|
||||
Each Python module is represented by a `.pyi` "stub file". This is a
|
||||
syntactically valid Python file, although it usually cannot be run by
|
||||
Python 3 (since forward references don't require string quotes). All
|
||||
the methods are empty.
|
||||
|
||||
Python function annotations ([PEP 3107](https://www.python.org/dev/peps/pep-3107/))
|
||||
are used to describe the signature of each function or method.
|
||||
|
||||
See [PEP 484](http://www.python.org/dev/peps/pep-0484/) for the exact
|
||||
syntax of the stub files and [CONTRIBUTING.md](CONTRIBUTING.md) for the
|
||||
coding style used in typeshed.
|
||||
|
||||
## Directory structure
|
||||
|
||||
### stdlib
|
||||
|
||||
This contains stubs for modules the Python standard library -- which
|
||||
includes pure Python modules, dynamically loaded extension modules,
|
||||
hard-linked extension modules, and the builtins.
|
||||
|
||||
### third_party
|
||||
|
||||
Modules that are not shipped with Python but have a type description in Python
|
||||
go into `third_party`. Since these modules can behave differently for different
|
||||
versions of Python, `third_party` has version subdirectories, just like
|
||||
`stdlib`.
|
||||
|
||||
NOTE: When you're contributing a new stub for a package that you did
|
||||
not develop, please obtain consent of the package owner (this is
|
||||
specified in [PEP
|
||||
484](https://www.python.org/dev/peps/pep-0484/#the-typeshed-repo)).
|
||||
The best way to obtain consent is to file an issue in the third-party
|
||||
package's tracker and include the link to a positive response in your PR
|
||||
for typeshed.
|
||||
|
||||
For more information on directory structure and stub versioning, see
|
||||
[the relevant section of CONTRIBUTING.md](
|
||||
https://github.com/python/typeshed/blob/master/CONTRIBUTING.md#stub-versioning).
|
||||
|
||||
## Contributing
|
||||
|
||||
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting pull
|
||||
requests. If you have questions related to contributing, drop by the [typing Gitter](https://gitter.im/python/typing).
|
||||
|
||||
## Running the tests
|
||||
|
||||
The tests are automatically run by Travis CI on every PR and push to
|
||||
the repo. There are several sets of tests: `tests/mypy_test.py`
|
||||
runs tests against [mypy](https://github.com/python/mypy/), while
|
||||
`tests/pytype_test.py` runs tests against
|
||||
[pytype](https://github.com/google/pytype/).
|
||||
|
||||
Both sets of tests are shallow -- they verify that all stubs can be
|
||||
imported but they don't check whether stubs match their implementation
|
||||
(in the Python standard library or a third-party package). Also note
|
||||
that each set of tests has a blacklist of modules that are not tested
|
||||
at all. The blacklists also live in the tests directory.
|
||||
|
||||
In addition, you can run `tests/mypy_selftest.py` to run mypy's own
|
||||
test suite using the typeshed code in your repo. This will sometimes
|
||||
catch issues with incorrectly typed stubs, but is much slower than the
|
||||
other tests.
|
||||
|
||||
To manually run the mypy tests, you need to have Python 3.5 or higher;
|
||||
Python 3.6.1 or higher is recommended.
|
||||
|
||||
Run:
|
||||
```
|
||||
$ python3.6 -m venv .venv3
|
||||
$ source .venv3/bin/activate
|
||||
(.venv3)$ pip3 install -r requirements-tests-py3.txt
|
||||
```
|
||||
This will install mypy (you need the latest master branch from GitHub),
|
||||
typed-ast, flake8, and pytype. You can then run mypy, flake8, and pytype tests
|
||||
by invoking:
|
||||
```
|
||||
(.venv3)$ python3 tests/mypy_test.py
|
||||
...
|
||||
(.venv3)$ python3 tests/mypy_selftest.py
|
||||
...
|
||||
(.venv3)$ flake8
|
||||
...
|
||||
(.venv3)$ python3 tests/pytype_test.py
|
||||
...
|
||||
```
|
||||
Note that flake8 only works with Python 3.6 or higher, and that to run the
|
||||
pytype tests, you will need Python 2.7 and Python 3.6 interpreters. Pytype will
|
||||
find these automatically if they're in `PATH`, but otherwise you must point to
|
||||
them with the `--python27-exe` and `--python36-exe` arguments, respectively.
|
||||
|
||||
These PyPI packages follow [the typing spec standards](https://typing.python.org/en/latest/spec/distributing.html)
|
||||
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 that is 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.
|
||||
For mypy, if you are in the typeshed repo that is submodule of the
|
||||
mypy repo (so `..` refers to the mypy repo), there's a shortcut to run
|
||||
the mypy tests that avoids installing mypy:
|
||||
```bash
|
||||
$ PYTHONPATH=../.. python3 tests/mypy_test.py
|
||||
```
|
||||
You can mypy tests to a single version by passing `-p2` or `-p3.5` e.g.
|
||||
```bash
|
||||
$ PYTHONPATH=../.. python3 tests/mypy_test.py -p3.5
|
||||
running mypy --python-version 3.5 --strict-optional # with 342 files
|
||||
```
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# Utilities for typeshed infrastructure scripts.
|
||||
|
||||
[project]
|
||||
name = "ts_utils"
|
||||
version = "0.0.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
@@ -1 +0,0 @@
|
||||
"""Utilities for typeshed infrastructure scripts."""
|
||||
@@ -1,433 +0,0 @@
|
||||
# This module is made specifically to abstract away those type errors
|
||||
# pyright: reportUnknownVariableType=false, reportUnknownArgumentType=false
|
||||
|
||||
"""Tools to help parse and validate information stored in METADATA.toml files."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
import urllib.parse
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Any, Final, NamedTuple, final
|
||||
from typing_extensions import TypeGuard
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
import tomllib
|
||||
else:
|
||||
import tomli as tomllib
|
||||
|
||||
import tomlkit
|
||||
from packaging.requirements import Requirement
|
||||
from packaging.specifiers import Specifier
|
||||
from tomlkit.items import String
|
||||
|
||||
from .paths import PYPROJECT_PATH, STUBS_PATH, distribution_path
|
||||
|
||||
__all__ = [
|
||||
"NoSuchStubError",
|
||||
"PackageDependencies",
|
||||
"StubMetadata",
|
||||
"StubtestSettings",
|
||||
"get_oldest_supported_python",
|
||||
"get_recursive_requirements",
|
||||
"read_dependencies",
|
||||
"read_metadata",
|
||||
"read_stubtest_settings",
|
||||
]
|
||||
|
||||
DEFAULT_STUBTEST_PLATFORMS = ["linux"]
|
||||
|
||||
_STUBTEST_PLATFORM_MAPPING: Final = {"linux": "apt_dependencies", "darwin": "brew_dependencies", "win32": "choco_dependencies"}
|
||||
# Some older websites have a bad pattern of using query params for navigation.
|
||||
_QUERY_URL_ALLOWLIST = {"sourceware.org"}
|
||||
|
||||
|
||||
def _is_list_of_strings(obj: object) -> TypeGuard[list[str]]:
|
||||
return isinstance(obj, list) and all(isinstance(item, str) for item in obj)
|
||||
|
||||
|
||||
def _is_nested_dict(obj: object) -> TypeGuard[dict[str, dict[str, Any]]]:
|
||||
return isinstance(obj, dict) and all(isinstance(k, str) and isinstance(v, dict) for k, v in obj.items())
|
||||
|
||||
|
||||
@functools.cache
|
||||
def get_oldest_supported_python() -> str:
|
||||
with PYPROJECT_PATH.open("rb") as config:
|
||||
val = tomllib.load(config)["tool"]["typeshed"]["oldest-supported-python"]
|
||||
assert type(val) is str
|
||||
return val
|
||||
|
||||
|
||||
def metadata_path(distribution: str) -> Path:
|
||||
"""Return the path to the METADATA.toml file of a third-party distribution."""
|
||||
return distribution_path(distribution) / "METADATA.toml"
|
||||
|
||||
|
||||
@final
|
||||
@dataclass(frozen=True)
|
||||
class StubtestSettings:
|
||||
"""The stubtest settings for a single stubs distribution.
|
||||
|
||||
Don't construct instances directly; use the `read_stubtest_settings` function.
|
||||
"""
|
||||
|
||||
skip: bool
|
||||
apt_dependencies: list[str]
|
||||
brew_dependencies: list[str]
|
||||
choco_dependencies: list[str]
|
||||
extras: list[str]
|
||||
ignore_missing_stub: bool
|
||||
supported_platforms: list[str] | None # None means all platforms
|
||||
ci_platforms: list[str]
|
||||
stubtest_dependencies: list[str]
|
||||
mypy_plugins: list[str]
|
||||
mypy_plugins_config: dict[str, dict[str, Any]]
|
||||
|
||||
def system_requirements_for_platform(self, platform: str) -> list[str]:
|
||||
assert platform in _STUBTEST_PLATFORM_MAPPING, f"Unrecognised platform {platform!r}"
|
||||
ret = getattr(self, _STUBTEST_PLATFORM_MAPPING[platform])
|
||||
assert _is_list_of_strings(ret)
|
||||
return ret
|
||||
|
||||
|
||||
@functools.cache
|
||||
def read_stubtest_settings(distribution: str) -> StubtestSettings:
|
||||
"""Return an object describing the stubtest settings for a single stubs distribution."""
|
||||
with metadata_path(distribution).open("rb") as f:
|
||||
data: dict[str, object] = tomllib.load(f).get("tool", {}).get("stubtest", {})
|
||||
|
||||
skip: object = data.get("skip", False)
|
||||
apt_dependencies: object = data.get("apt-dependencies", [])
|
||||
brew_dependencies: object = data.get("brew-dependencies", [])
|
||||
choco_dependencies: object = data.get("choco-dependencies", [])
|
||||
extras: object = data.get("extras", [])
|
||||
ignore_missing_stub: object = data.get("ignore-missing-stub", False)
|
||||
supported_platforms: object = data.get("supported-platforms")
|
||||
ci_platforms: object = data.get("ci-platforms", DEFAULT_STUBTEST_PLATFORMS)
|
||||
stubtest_dependencies: object = data.get("stubtest-dependencies", [])
|
||||
mypy_plugins: object = data.get("mypy-plugins", [])
|
||||
mypy_plugins_config: object = data.get("mypy-plugins-config", {})
|
||||
|
||||
assert type(skip) is bool
|
||||
assert type(ignore_missing_stub) is bool
|
||||
|
||||
# It doesn't work for type-narrowing if we use a for loop here...
|
||||
assert supported_platforms is None or _is_list_of_strings(supported_platforms)
|
||||
assert _is_list_of_strings(ci_platforms)
|
||||
assert _is_list_of_strings(apt_dependencies)
|
||||
assert _is_list_of_strings(brew_dependencies)
|
||||
assert _is_list_of_strings(choco_dependencies)
|
||||
assert _is_list_of_strings(extras)
|
||||
assert _is_list_of_strings(stubtest_dependencies)
|
||||
assert _is_list_of_strings(mypy_plugins)
|
||||
assert _is_nested_dict(mypy_plugins_config)
|
||||
|
||||
unrecognised_platforms = set(ci_platforms) - _STUBTEST_PLATFORM_MAPPING.keys()
|
||||
assert not unrecognised_platforms, f"Unrecognised ci-platforms specified for {distribution!r}: {unrecognised_platforms}"
|
||||
|
||||
if supported_platforms is not None:
|
||||
assert set(ci_platforms).issubset(
|
||||
supported_platforms
|
||||
), f"ci-platforms must be a subset of supported-platforms for {distribution!r}"
|
||||
|
||||
for platform, dep_key in _STUBTEST_PLATFORM_MAPPING.items():
|
||||
if platform not in ci_platforms:
|
||||
assert dep_key not in data, (
|
||||
f"Stubtest is not run on {platform} in CI for {distribution!r}, "
|
||||
f"but {dep_key!r} are specified in METADATA.toml"
|
||||
)
|
||||
|
||||
return StubtestSettings(
|
||||
skip=skip,
|
||||
apt_dependencies=apt_dependencies,
|
||||
brew_dependencies=brew_dependencies,
|
||||
choco_dependencies=choco_dependencies,
|
||||
extras=extras,
|
||||
ignore_missing_stub=ignore_missing_stub,
|
||||
supported_platforms=supported_platforms,
|
||||
ci_platforms=ci_platforms,
|
||||
stubtest_dependencies=stubtest_dependencies,
|
||||
mypy_plugins=mypy_plugins,
|
||||
mypy_plugins_config=mypy_plugins_config,
|
||||
)
|
||||
|
||||
|
||||
@final
|
||||
@dataclass(frozen=True)
|
||||
class ObsoleteMetadata:
|
||||
since_version: Annotated[str, "A string representing a specific version"]
|
||||
since_date: Annotated[datetime.date, "A date when the package became obsolete"]
|
||||
|
||||
|
||||
@final
|
||||
@dataclass(frozen=True)
|
||||
class StubMetadata:
|
||||
"""The metadata for a single stubs distribution.
|
||||
|
||||
Don't construct instances directly; use the `read_metadata` function.
|
||||
"""
|
||||
|
||||
distribution: Annotated[str, "The name of the distribution on PyPI"]
|
||||
version_spec: Annotated[Specifier, "Upstream versions that the stubs are compatible with"]
|
||||
dependencies: Annotated[list[Requirement], "The parsed dependencies as listed in METADATA.toml"]
|
||||
extra_description: str | None
|
||||
stub_distribution: Annotated[str, "The name under which the distribution is uploaded to PyPI"]
|
||||
upstream_repository: Annotated[str, "The URL of the upstream repository"] | None
|
||||
obsolete: Annotated[ObsoleteMetadata, "Metadata indicating when the stubs package became obsolete"] | None
|
||||
no_longer_updated: bool
|
||||
uploaded_to_pypi: Annotated[bool, "Whether or not a distribution is uploaded to PyPI"]
|
||||
partial_stub: Annotated[bool, "Whether this is a partial type stub package as per PEP 561."]
|
||||
stubtest_settings: StubtestSettings
|
||||
requires_python: Annotated[Specifier, "Versions of Python supported by the stub package"]
|
||||
|
||||
@property
|
||||
def is_obsolete(self) -> bool:
|
||||
return self.obsolete is not None
|
||||
|
||||
|
||||
_KNOWN_METADATA_FIELDS: Final = frozenset(
|
||||
{
|
||||
"version",
|
||||
"dependencies",
|
||||
"extra-description",
|
||||
"stub-distribution",
|
||||
"upstream-repository",
|
||||
"obsolete-since",
|
||||
"no-longer-updated",
|
||||
"upload",
|
||||
"tool",
|
||||
"partial-stub",
|
||||
"requires-python",
|
||||
"mypy-tests",
|
||||
}
|
||||
)
|
||||
_KNOWN_METADATA_TOOL_FIELDS: Final = {
|
||||
"stubtest": {
|
||||
"skip",
|
||||
"apt-dependencies",
|
||||
"brew-dependencies",
|
||||
"choco-dependencies",
|
||||
"extras",
|
||||
"ignore-missing-stub",
|
||||
"supported-platforms",
|
||||
"ci-platforms",
|
||||
"stubtest-dependencies",
|
||||
"mypy-plugins",
|
||||
"mypy-plugins-config",
|
||||
}
|
||||
}
|
||||
_DIST_NAME_RE: Final = re.compile(r"^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$", re.IGNORECASE)
|
||||
|
||||
|
||||
class NoSuchStubError(ValueError):
|
||||
"""Raise NoSuchStubError to indicate that a stubs/{distribution} directory doesn't exist."""
|
||||
|
||||
|
||||
@functools.cache
|
||||
def read_metadata(distribution: str) -> StubMetadata:
|
||||
"""Return an object describing the metadata of a stub as given in the METADATA.toml file.
|
||||
|
||||
This function does some basic validation,
|
||||
but does no parsing, transforming or normalization of the metadata.
|
||||
Use `read_dependencies` if you need to parse the dependencies
|
||||
given in the `dependencies` field, for example.
|
||||
"""
|
||||
try:
|
||||
with metadata_path(distribution).open("rb") as f:
|
||||
data = tomlkit.load(f)
|
||||
except FileNotFoundError:
|
||||
raise NoSuchStubError(f"Typeshed has no stubs for {distribution!r}!") from None
|
||||
|
||||
unknown_metadata_fields = data.keys() - _KNOWN_METADATA_FIELDS
|
||||
assert not unknown_metadata_fields, f"Unexpected keys in METADATA.toml for {distribution!r}: {unknown_metadata_fields}"
|
||||
|
||||
assert "version" in data, f"Missing 'version' field in METADATA.toml for {distribution!r}"
|
||||
version: object = data.get("version") # pyright: ignore[reportUnknownMemberType]
|
||||
assert isinstance(version, str) and len(version) > 0, f"Invalid 'version' field in METADATA.toml for {distribution!r}"
|
||||
# Check that the version spec parses
|
||||
if version[0].isdigit():
|
||||
version = f"=={version}"
|
||||
version_spec = Specifier(version)
|
||||
assert version_spec.operator in {"==", "~="}, f"Invalid 'version' field in METADATA.toml for {distribution!r}"
|
||||
|
||||
dependencies_s: object = data.get("dependencies", []) # pyright: ignore[reportUnknownMemberType]
|
||||
assert isinstance(dependencies_s, list)
|
||||
dependencies = [parse_dependencies(distribution, dep) for dep in dependencies_s]
|
||||
|
||||
extra_description: object = data.get("extra-description") # pyright: ignore[reportUnknownMemberType]
|
||||
assert isinstance(extra_description, (str, type(None)))
|
||||
|
||||
if "stub-distribution" in data:
|
||||
stub_distribution = data["stub-distribution"]
|
||||
assert isinstance(stub_distribution, str)
|
||||
assert _DIST_NAME_RE.fullmatch(stub_distribution), f"Invalid 'stub-distribution' value for {distribution!r}"
|
||||
else:
|
||||
stub_distribution = f"types-{distribution}"
|
||||
|
||||
upstream_repository: object = data.get("upstream-repository") # pyright: ignore[reportUnknownMemberType]
|
||||
assert isinstance(upstream_repository, (str, type(None)))
|
||||
if isinstance(upstream_repository, str):
|
||||
parsed_url = urllib.parse.urlsplit(upstream_repository)
|
||||
assert parsed_url.scheme == "https", f"{distribution}: URLs in the upstream-repository field should use https"
|
||||
no_www_please = (
|
||||
f"{distribution}: `World Wide Web` subdomain (`www.`) should be removed from URLs in the upstream-repository field"
|
||||
)
|
||||
assert not parsed_url.netloc.startswith("www."), no_www_please
|
||||
no_query_params_please = (
|
||||
f"{distribution}: Query params (`?`) should be removed from URLs in the upstream-repository field"
|
||||
)
|
||||
assert parsed_url.hostname in _QUERY_URL_ALLOWLIST or (not parsed_url.query), no_query_params_please
|
||||
no_fragments_please = f"{distribution}: Fragments (`#`) should be removed from URLs in the upstream-repository field"
|
||||
assert not parsed_url.fragment, no_fragments_please
|
||||
if parsed_url.netloc == "github.com":
|
||||
cleaned_url_path = parsed_url.path.strip("/")
|
||||
num_url_path_parts = len(Path(cleaned_url_path).parts)
|
||||
bad_github_url_msg = (
|
||||
f"Invalid upstream-repository for {distribution!r}: "
|
||||
"URLs for GitHub repositories always have two parts in their paths"
|
||||
)
|
||||
assert num_url_path_parts == 2, bad_github_url_msg
|
||||
|
||||
obsolete_since: object = data.get("obsolete-since") # pyright: ignore[reportUnknownMemberType]
|
||||
assert isinstance(obsolete_since, (String, type(None)))
|
||||
if obsolete_since:
|
||||
comment = obsolete_since.trivia.comment
|
||||
since_date_string = comment.removeprefix("# Released on ")
|
||||
since_date = datetime.date.fromisoformat(since_date_string)
|
||||
obsolete = ObsoleteMetadata(since_version=obsolete_since, since_date=since_date)
|
||||
else:
|
||||
obsolete = None
|
||||
no_longer_updated: object = data.get("no-longer-updated", False) # pyright: ignore[reportUnknownMemberType]
|
||||
assert type(no_longer_updated) is bool
|
||||
uploaded_to_pypi: object = data.get("upload", True) # pyright: ignore[reportUnknownMemberType]
|
||||
assert type(uploaded_to_pypi) is bool
|
||||
partial_stub: object = data.get("partial-stub", True) # pyright: ignore[reportUnknownMemberType]
|
||||
assert type(partial_stub) is bool
|
||||
requires_python_str: object = data.get("requires-python") # pyright: ignore[reportUnknownMemberType]
|
||||
oldest_supported_python = get_oldest_supported_python()
|
||||
oldest_supported_python_specifier = Specifier(f">={oldest_supported_python}")
|
||||
if requires_python_str is None:
|
||||
requires_python = oldest_supported_python_specifier
|
||||
else:
|
||||
assert isinstance(requires_python_str, str)
|
||||
requires_python = Specifier(requires_python_str)
|
||||
assert requires_python != oldest_supported_python_specifier, f'requires-python="{requires_python}" is redundant'
|
||||
# Check minimum Python version is not less than the oldest version of Python supported by typeshed
|
||||
assert oldest_supported_python_specifier.contains(
|
||||
requires_python.version
|
||||
), f"'requires-python' contains versions lower than typeshed's oldest supported Python ({oldest_supported_python})"
|
||||
assert requires_python.operator == ">=", "'requires-python' should be a minimum version specifier, use '>=3.x'"
|
||||
|
||||
empty_tools: dict[object, object] = {}
|
||||
tools_settings: object = data.get("tool", empty_tools) # pyright: ignore[reportUnknownMemberType]
|
||||
assert isinstance(tools_settings, dict)
|
||||
assert tools_settings.keys() <= _KNOWN_METADATA_TOOL_FIELDS.keys(), f"Unrecognised tool for {distribution!r}"
|
||||
for tool, tk in _KNOWN_METADATA_TOOL_FIELDS.items():
|
||||
settings_for_tool: object = tools_settings.get(tool, {}) # pyright: ignore[reportUnknownMemberType]
|
||||
assert isinstance(settings_for_tool, dict)
|
||||
for key in settings_for_tool:
|
||||
assert key in tk, f"Unrecognised {tool} key {key!r} for {distribution!r}"
|
||||
|
||||
return StubMetadata(
|
||||
distribution=distribution,
|
||||
version_spec=version_spec,
|
||||
dependencies=dependencies,
|
||||
extra_description=extra_description,
|
||||
stub_distribution=stub_distribution,
|
||||
upstream_repository=upstream_repository,
|
||||
obsolete=obsolete,
|
||||
no_longer_updated=no_longer_updated,
|
||||
uploaded_to_pypi=uploaded_to_pypi,
|
||||
partial_stub=partial_stub,
|
||||
stubtest_settings=read_stubtest_settings(distribution),
|
||||
requires_python=requires_python,
|
||||
)
|
||||
|
||||
|
||||
def update_metadata(distribution: str, **new_values: object) -> tomlkit.TOMLDocument:
|
||||
"""Update a distribution's METADATA.toml.
|
||||
|
||||
Return the updated TOML dictionary for use without having to open the file separately.
|
||||
"""
|
||||
path = metadata_path(distribution)
|
||||
try:
|
||||
with path.open("rb") as file:
|
||||
data = tomlkit.load(file)
|
||||
except FileNotFoundError:
|
||||
raise NoSuchStubError(f"Typeshed has no stubs for {distribution!r}!") from None
|
||||
data.update(new_values) # pyright: ignore[reportUnknownMemberType] # tomlkit.TOMLDocument.update is partially typed
|
||||
for key in list(data.keys()):
|
||||
new_key = key.replace("_", "-") # pyright: ignore[reportUnknownMemberType] # tomlkit.TOMLDocument.keys is partially typed
|
||||
data[new_key] = data.pop(key) # pyright: ignore[reportUnknownMemberType] # tomlkit.TOMLDocument.pop is partially typed
|
||||
with path.open("w", encoding="UTF-8") as file:
|
||||
tomlkit.dump(data, file) # pyright: ignore[reportUnknownMemberType] # tomlkit.dump has partially unknown Mapping type
|
||||
return data
|
||||
|
||||
|
||||
def parse_dependencies(distribution: str, req: object) -> Requirement:
|
||||
assert isinstance(req, str), f"Invalid requirement {req!r} for {distribution!r}"
|
||||
return Requirement(req)
|
||||
|
||||
|
||||
class PackageDependencies(NamedTuple):
|
||||
typeshed_pkgs: tuple[Requirement, ...]
|
||||
external_pkgs: tuple[Requirement, ...]
|
||||
|
||||
|
||||
@functools.cache
|
||||
def get_pypi_name_to_typeshed_name_mapping() -> Mapping[str, str]:
|
||||
return {read_metadata(stub_dir.name).stub_distribution: stub_dir.name for stub_dir in STUBS_PATH.iterdir()}
|
||||
|
||||
|
||||
@functools.cache
|
||||
def read_dependencies(distribution: str) -> PackageDependencies:
|
||||
"""Read the dependencies listed in a METADATA.toml file for a stubs package.
|
||||
|
||||
Once the dependencies have been read,
|
||||
determine which dependencies are typeshed-internal dependencies,
|
||||
and which dependencies are external (non-types) dependencies.
|
||||
For typeshed dependencies, translate the "dependency name" into the "package name";
|
||||
for external dependencies, leave them as they are in the METADATA.toml file.
|
||||
|
||||
Note that this function may consider things to be typeshed stubs
|
||||
even if they haven't yet been uploaded to PyPI.
|
||||
If a typeshed stub is removed, this function will consider it to be an external dependency.
|
||||
"""
|
||||
pypi_name_to_typeshed_name_mapping = get_pypi_name_to_typeshed_name_mapping()
|
||||
typeshed: list[Requirement] = []
|
||||
external: list[Requirement] = []
|
||||
for dependency in read_metadata(distribution).dependencies:
|
||||
if dependency.name in pypi_name_to_typeshed_name_mapping:
|
||||
req = Requirement(str(dependency)) # copy the requirement
|
||||
req.name = pypi_name_to_typeshed_name_mapping[dependency.name]
|
||||
typeshed.append(req)
|
||||
else:
|
||||
external.append(dependency)
|
||||
return PackageDependencies(tuple(typeshed), tuple(external))
|
||||
|
||||
|
||||
@functools.cache
|
||||
def get_recursive_requirements(package_name: str) -> PackageDependencies:
|
||||
"""Recursively gather dependencies for a single stubs package.
|
||||
|
||||
For example, if the stubs for `caldav`
|
||||
declare a dependency on typeshed's stubs for `requests`,
|
||||
and the stubs for requests declare a dependency on typeshed's stubs for `urllib3`,
|
||||
`get_recursive_requirements("caldav")` will determine that the stubs for `caldav`
|
||||
have both `requests` and `urllib3` as typeshed-internal dependencies.
|
||||
"""
|
||||
typeshed: set[Requirement] = set()
|
||||
external: set[Requirement] = set()
|
||||
non_recursive_requirements = read_dependencies(package_name)
|
||||
typeshed.update(non_recursive_requirements.typeshed_pkgs)
|
||||
external.update(non_recursive_requirements.external_pkgs)
|
||||
for pkg in non_recursive_requirements.typeshed_pkgs:
|
||||
reqs = get_recursive_requirements(pkg.name)
|
||||
typeshed.update(reqs.typeshed_pkgs)
|
||||
external.update(reqs.external_pkgs)
|
||||
return PackageDependencies(tuple(typeshed), tuple(external))
|
||||
@@ -1,81 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from collections.abc import Generator, Iterable
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, NamedTuple
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
import tomllib
|
||||
else:
|
||||
import tomli as tomllib
|
||||
|
||||
from ts_utils.metadata import StubtestSettings, metadata_path
|
||||
from ts_utils.utils import NamedTemporaryFile, TemporaryFileWrapper
|
||||
|
||||
|
||||
class MypyDistConf(NamedTuple):
|
||||
module_name: str
|
||||
values: dict[str, dict[str, Any]]
|
||||
|
||||
|
||||
# The configuration section in the metadata file looks like the following, with multiple module sections possible
|
||||
# [mypy-tests]
|
||||
# [mypy-tests.yaml]
|
||||
# module_name = "yaml"
|
||||
# [mypy-tests.yaml.values]
|
||||
# disallow_incomplete_defs = true
|
||||
# disallow_untyped_defs = true
|
||||
|
||||
|
||||
def mypy_configuration_from_distribution(distribution: str) -> list[MypyDistConf]:
|
||||
with metadata_path(distribution).open("rb") as f:
|
||||
data = tomllib.load(f)
|
||||
|
||||
# TODO: This could be added to ts_utils.metadata
|
||||
mypy_tests_conf: dict[str, dict[str, Any]] = data.get("mypy-tests", {})
|
||||
if not mypy_tests_conf:
|
||||
return []
|
||||
|
||||
def validate_configuration(section_name: str, mypy_section: dict[str, Any]) -> MypyDistConf:
|
||||
assert isinstance(mypy_section, dict), f"{section_name} should be a section"
|
||||
module_name = mypy_section.get("module_name")
|
||||
|
||||
assert module_name is not None, f"{section_name} should have a module_name key"
|
||||
assert isinstance(module_name, str), f"{section_name} should be a key-value pair"
|
||||
|
||||
assert "values" in mypy_section, f"{section_name} should have a values section"
|
||||
values: dict[str, dict[str, Any]] = mypy_section["values"]
|
||||
assert isinstance(values, dict), "values should be a section"
|
||||
return MypyDistConf(module_name, values.copy())
|
||||
|
||||
assert isinstance(mypy_tests_conf, dict), "mypy-tests should be a section"
|
||||
return [validate_configuration(section_name, mypy_section) for section_name, mypy_section in mypy_tests_conf.items()]
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temporary_mypy_config_file(
|
||||
configurations: Iterable[MypyDistConf], stubtest_settings: StubtestSettings | None = None
|
||||
) -> Generator[TemporaryFileWrapper[str]]:
|
||||
temp = NamedTemporaryFile("w+")
|
||||
try:
|
||||
for dist_conf in configurations:
|
||||
temp.write(f"[mypy-{dist_conf.module_name}]\n")
|
||||
for k, v in dist_conf.values.items():
|
||||
temp.write(f"{k} = {v}\n")
|
||||
temp.write("[mypy]\n")
|
||||
|
||||
if stubtest_settings:
|
||||
if stubtest_settings.mypy_plugins:
|
||||
temp.write(f"plugins = {'.'.join(stubtest_settings.mypy_plugins)}\n")
|
||||
|
||||
if stubtest_settings.mypy_plugins_config:
|
||||
for plugin_name, plugin_dict in stubtest_settings.mypy_plugins_config.items():
|
||||
temp.write(f"[mypy.plugins.{plugin_name}]\n")
|
||||
for k, v in plugin_dict.items():
|
||||
temp.write(f"{k} = {v}\n")
|
||||
|
||||
temp.flush()
|
||||
yield temp
|
||||
finally:
|
||||
temp.close()
|
||||
@@ -1,41 +0,0 @@
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
# TODO: Use base path relative to this file. Currently, ts_utils gets
|
||||
# installed into the user's virtual env, so we can't determine the path
|
||||
# to typeshed. Installing ts_utils editable would solve that, see
|
||||
# https://github.com/python/typeshed/pull/12806.
|
||||
TS_BASE_PATH: Final = Path()
|
||||
STDLIB_PATH: Final = TS_BASE_PATH / "stdlib"
|
||||
STUBS_PATH: Final = TS_BASE_PATH / "stubs"
|
||||
|
||||
PYPROJECT_PATH: Final = TS_BASE_PATH / "pyproject.toml"
|
||||
REQUIREMENTS_PATH: Final = TS_BASE_PATH / "requirements-tests.txt"
|
||||
GITIGNORE_PATH: Final = TS_BASE_PATH / ".gitignore"
|
||||
PYRIGHT_CONFIG: Final = TS_BASE_PATH / "pyrightconfig.stricter.json"
|
||||
|
||||
TESTS_DIR: Final = "@tests"
|
||||
TEST_CASES_DIR: Final = "test_cases"
|
||||
|
||||
|
||||
def distribution_path(distribution_name: str) -> Path:
|
||||
"""Return the path to the directory of a third-party distribution."""
|
||||
return STUBS_PATH / distribution_name
|
||||
|
||||
|
||||
def tests_path(distribution_name: str) -> Path:
|
||||
if distribution_name == "stdlib":
|
||||
return STDLIB_PATH / TESTS_DIR
|
||||
else:
|
||||
return STUBS_PATH / distribution_name / TESTS_DIR
|
||||
|
||||
|
||||
def test_cases_path(distribution_name: str) -> Path:
|
||||
return tests_path(distribution_name) / TEST_CASES_DIR
|
||||
|
||||
|
||||
def allowlists_path(distribution_name: str) -> Path:
|
||||
if distribution_name == "stdlib":
|
||||
return tests_path("stdlib") / "stubtest_allowlists"
|
||||
else:
|
||||
return tests_path(distribution_name)
|
||||
@@ -1,29 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import Iterable
|
||||
|
||||
from packaging.requirements import Requirement
|
||||
|
||||
from ts_utils.metadata import read_dependencies, read_stubtest_settings
|
||||
from ts_utils.paths import STUBS_PATH
|
||||
|
||||
|
||||
def get_external_stub_requirements(distributions: Iterable[str] = ()) -> set[Requirement]:
|
||||
if not distributions:
|
||||
distributions = os.listdir(STUBS_PATH)
|
||||
|
||||
return set(itertools.chain.from_iterable([read_dependencies(distribution).external_pkgs for distribution in distributions]))
|
||||
|
||||
|
||||
def get_stubtest_system_requirements(distributions: Iterable[str] = (), platform: str = sys.platform) -> set[str]:
|
||||
if not distributions:
|
||||
distributions = os.listdir(STUBS_PATH)
|
||||
|
||||
return set(
|
||||
itertools.chain.from_iterable(
|
||||
[read_stubtest_settings(distribution).system_requirements_for_platform(platform) for distribution in distributions]
|
||||
)
|
||||
)
|
||||
@@ -1,287 +0,0 @@
|
||||
"""Utilities that are imported by multiple scripts in the tests directory."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
from collections.abc import Iterable, Mapping
|
||||
from pathlib import Path
|
||||
from types import MethodType
|
||||
from typing import TYPE_CHECKING, Any, Final, NamedTuple
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
import pathspec
|
||||
from packaging.requirements import Requirement
|
||||
|
||||
from .paths import GITIGNORE_PATH, REQUIREMENTS_PATH, STDLIB_PATH, STUBS_PATH, TEST_CASES_DIR, allowlists_path, test_cases_path
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import OpenTextMode
|
||||
|
||||
try:
|
||||
from termcolor import colored as colored # pyright: ignore[reportAssignmentType]
|
||||
except ImportError:
|
||||
|
||||
def colored(text: str, color: str | None = None, **kwargs: Any) -> str: # type: ignore[misc] # noqa: ARG001
|
||||
return text
|
||||
|
||||
|
||||
_REMOVE_COMMENT_RE = re.compile(
|
||||
r"""
|
||||
(\"(?:\\.|[^\\\"])*?\") # matches literal strings
|
||||
|
|
||||
(\/\*.*?\*\/ | \/\/[^\r\n]*?(?:[\r\n])) # matches single- and multi-line comments
|
||||
""",
|
||||
re.DOTALL | re.VERBOSE,
|
||||
)
|
||||
_REMOVE_TRAILING_COMMA_RE = re.compile(
|
||||
r"""
|
||||
(\"(?:\\.|[^\\\"])*?\") # matches literal strings
|
||||
|
|
||||
,\s*([\]}]) # matches commas before '}' or ']'
|
||||
""",
|
||||
re.DOTALL | re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
PYTHON_VERSION: Final = f"{sys.version_info.major}.{sys.version_info.minor}"
|
||||
|
||||
|
||||
def strip_comments(text: str) -> str:
|
||||
return text.split("#", maxsplit=1)[0].strip()
|
||||
|
||||
|
||||
def jsonc_to_json(text: str) -> str:
|
||||
"""Conversion from JSONC format input to valid JSON."""
|
||||
# Remove comments
|
||||
if not text.endswith("\n"):
|
||||
text += "\n"
|
||||
text = _REMOVE_COMMENT_RE.sub(lambda m: m.group(1) or "", text)
|
||||
|
||||
# Remove trailing commas before } or ]
|
||||
text = _REMOVE_TRAILING_COMMA_RE.sub(lambda m: m.group(1) or m.group(2), text)
|
||||
return text
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# Printing utilities
|
||||
# ====================================================================
|
||||
|
||||
|
||||
def print_command(cmd: str | Iterable[str]) -> None:
|
||||
if not isinstance(cmd, str):
|
||||
cmd = " ".join(cmd)
|
||||
print(colored(f"Running: {cmd}", "blue"))
|
||||
|
||||
|
||||
def print_info(message: str) -> None:
|
||||
print(colored(message, "blue"))
|
||||
|
||||
|
||||
def print_warning(message: str) -> None:
|
||||
print(colored(message, "yellow"))
|
||||
|
||||
|
||||
def print_error(error: str, end: str = "\n", fix_path: tuple[str, str] = ("", "")) -> None:
|
||||
error_split = error.split("\n")
|
||||
old, new = fix_path
|
||||
for line in error_split[:-1]:
|
||||
print(colored(line.replace(old, new), "red"))
|
||||
print(colored(error_split[-1], "red"), end=end)
|
||||
|
||||
|
||||
def print_success_msg() -> None:
|
||||
print(colored("success", "green"))
|
||||
|
||||
|
||||
def print_divider() -> None:
|
||||
"""Print a row of * symbols across the screen.
|
||||
|
||||
This can be useful to divide terminal output into separate sections.
|
||||
"""
|
||||
print()
|
||||
print("*" * 70)
|
||||
print()
|
||||
|
||||
|
||||
def print_time(t: float) -> None:
|
||||
print(f"({t:.2f} s) ", end="")
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# Dynamic venv creation
|
||||
# ====================================================================
|
||||
|
||||
|
||||
@functools.cache
|
||||
def venv_python(venv_dir: Path) -> Path:
|
||||
if sys.platform == "win32":
|
||||
return venv_dir / "Scripts" / "python.exe"
|
||||
return venv_dir / "bin" / "python"
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# Parsing the requirements file
|
||||
# ====================================================================
|
||||
|
||||
|
||||
@functools.cache
|
||||
def parse_requirements() -> Mapping[str, Requirement]:
|
||||
"""Return a dictionary of requirements from the requirements file."""
|
||||
with REQUIREMENTS_PATH.open(encoding="UTF-8") as requirements_file:
|
||||
stripped_lines = map(strip_comments, requirements_file)
|
||||
stripped_more = [li for li in stripped_lines if not li.startswith("-")]
|
||||
requirements = map(Requirement, filter(None, stripped_more))
|
||||
return {requirement.name: requirement for requirement in requirements}
|
||||
|
||||
|
||||
def get_mypy_req() -> str:
|
||||
return str(parse_requirements()["mypy"])
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# Parsing the stdlib/VERSIONS file
|
||||
# ====================================================================
|
||||
|
||||
VersionTuple: TypeAlias = tuple[int, int]
|
||||
SupportedVersionsDict: TypeAlias = dict[str, tuple[VersionTuple, VersionTuple]]
|
||||
|
||||
VERSIONS_PATH = STDLIB_PATH / "VERSIONS"
|
||||
VERSION_LINE_RE = re.compile(r"^([a-zA-Z_][a-zA-Z0-9_.]*): ([23]\.\d{1,2})-([23]\.\d{1,2})?$")
|
||||
VERSION_RE = re.compile(r"^([23])\.(\d+)$")
|
||||
|
||||
|
||||
def parse_stdlib_versions_file() -> SupportedVersionsDict:
|
||||
result: dict[str, tuple[VersionTuple, VersionTuple]] = {}
|
||||
with VERSIONS_PATH.open(encoding="UTF-8") as f:
|
||||
for line in f:
|
||||
stripped_line = strip_comments(line)
|
||||
if stripped_line == "":
|
||||
continue
|
||||
m = VERSION_LINE_RE.match(stripped_line)
|
||||
assert m, f"invalid VERSIONS line: {stripped_line}"
|
||||
mod: str = m.group(1)
|
||||
assert mod not in result, f"Duplicate module {mod} in VERSIONS"
|
||||
min_version = _parse_version(m.group(2))
|
||||
max_version = _parse_version(m.group(3)) if m.group(3) else (99, 99)
|
||||
result[mod] = min_version, max_version
|
||||
return result
|
||||
|
||||
|
||||
def supported_versions_for_module(module_versions: SupportedVersionsDict, module_name: str) -> tuple[VersionTuple, VersionTuple]:
|
||||
while "." in module_name:
|
||||
if module_name in module_versions:
|
||||
return module_versions[module_name]
|
||||
module_name = ".".join(module_name.split(".")[:-1])
|
||||
return module_versions[module_name]
|
||||
|
||||
|
||||
def _parse_version(v_str: str) -> tuple[int, int]:
|
||||
m = VERSION_RE.match(v_str)
|
||||
assert m, f"invalid version: {v_str}"
|
||||
return int(m.group(1)), int(m.group(2))
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# Test Directories
|
||||
# ====================================================================
|
||||
|
||||
|
||||
class DistributionTests(NamedTuple):
|
||||
name: str
|
||||
test_cases_path: Path
|
||||
|
||||
@property
|
||||
def is_stdlib(self) -> bool:
|
||||
return self.name == "stdlib"
|
||||
|
||||
|
||||
def distribution_info(distribution_name: str) -> DistributionTests:
|
||||
if distribution_name == "stdlib":
|
||||
return DistributionTests("stdlib", test_cases_path("stdlib"))
|
||||
test_path = test_cases_path(distribution_name)
|
||||
if test_path.is_dir():
|
||||
if not list(test_path.iterdir()):
|
||||
raise RuntimeError(f"{distribution_name!r} has a '{TEST_CASES_DIR}' directory but it is empty!")
|
||||
return DistributionTests(distribution_name, test_path)
|
||||
raise RuntimeError(f"No test cases found for {distribution_name!r}!")
|
||||
|
||||
|
||||
def get_all_testcase_directories() -> list[DistributionTests]:
|
||||
testcase_directories: list[DistributionTests] = []
|
||||
for distribution_path in STUBS_PATH.iterdir():
|
||||
try:
|
||||
pkg_info = distribution_info(distribution_path.name)
|
||||
except RuntimeError:
|
||||
continue
|
||||
testcase_directories.append(pkg_info)
|
||||
return [distribution_info("stdlib"), *sorted(testcase_directories)]
|
||||
|
||||
|
||||
def allowlists(distribution_name: str) -> list[str]:
|
||||
prefix = "" if distribution_name == "stdlib" else "stubtest_allowlist_"
|
||||
version_id = f"py{sys.version_info.major}{sys.version_info.minor}"
|
||||
|
||||
platform_allowlist = f"{prefix}{sys.platform}.txt"
|
||||
version_allowlist = f"{prefix}{version_id}.txt"
|
||||
combined_allowlist = f"{prefix}{sys.platform}-{version_id}.txt"
|
||||
local_version_allowlist = version_allowlist + ".local"
|
||||
|
||||
if distribution_name == "stdlib":
|
||||
return ["common.txt", platform_allowlist, version_allowlist, combined_allowlist, local_version_allowlist]
|
||||
else:
|
||||
return ["stubtest_allowlist.txt", platform_allowlist]
|
||||
|
||||
|
||||
# Re-exposing as a public name to avoid many pyright reportPrivateUsage
|
||||
TemporaryFileWrapper = tempfile._TemporaryFileWrapper # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
# We need to work around a limitation of tempfile.NamedTemporaryFile on Windows
|
||||
# For details, see https://github.com/python/typeshed/pull/13620#discussion_r1990185997
|
||||
# Python 3.12 added a cross-platform solution with `tempfile.NamedTemporaryFile("w+", delete_on_close=False)`
|
||||
if sys.platform != "win32":
|
||||
NamedTemporaryFile = tempfile.NamedTemporaryFile # noqa: TID251
|
||||
else:
|
||||
|
||||
def NamedTemporaryFile(mode: OpenTextMode) -> TemporaryFileWrapper[str]: # noqa: N802
|
||||
def close(self: TemporaryFileWrapper[str]) -> None:
|
||||
TemporaryFileWrapper.close(self) # pyright: ignore[reportUnknownMemberType]
|
||||
Path(self.name).unlink()
|
||||
|
||||
temp = tempfile.NamedTemporaryFile(mode, delete=False) # noqa: SIM115, TID251
|
||||
temp.close = MethodType(close, temp) # type: ignore[method-assign]
|
||||
return temp
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# Parsing .gitignore
|
||||
# ====================================================================
|
||||
|
||||
|
||||
@functools.cache
|
||||
def get_gitignore_spec() -> pathspec.PathSpec:
|
||||
with GITIGNORE_PATH.open(encoding="UTF-8") as f:
|
||||
return pathspec.GitIgnoreSpec.from_lines(f)
|
||||
|
||||
|
||||
def spec_matches_path(spec: pathspec.PathSpec, path: Path) -> bool:
|
||||
normalized_path = path.as_posix()
|
||||
if path.is_dir():
|
||||
normalized_path += "/"
|
||||
return spec.match_file(normalized_path)
|
||||
|
||||
|
||||
# ====================================================================
|
||||
# stubtest call
|
||||
# ====================================================================
|
||||
|
||||
|
||||
def allowlist_stubtest_arguments(distribution_name: str) -> list[str]:
|
||||
stubtest_arguments: list[str] = []
|
||||
for allowlist in allowlists(distribution_name):
|
||||
path = allowlists_path(distribution_name) / allowlist
|
||||
if path.exists():
|
||||
stubtest_arguments.extend(["--allowlist", str(path)])
|
||||
return stubtest_arguments
|
||||
+9
-266
@@ -1,268 +1,11 @@
|
||||
[project]
|
||||
# This section is needed to avoid writing --no-project everytime when using "uv run"
|
||||
# https://github.com/astral-sh/uv/issues/8666
|
||||
name = "typeshed"
|
||||
version = "0"
|
||||
requires-python = ">=3.10" # Minimum version to run tests, used by uv run
|
||||
|
||||
[tool.black]
|
||||
line-length = 130
|
||||
target-version = ["py310"]
|
||||
skip-magic-trailing-comma = true
|
||||
line_length = 130
|
||||
target_version = ["py37"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 130
|
||||
fix = true
|
||||
exclude = [
|
||||
# virtual environment
|
||||
".env",
|
||||
".venv",
|
||||
"env",
|
||||
# cache directories, etc.:
|
||||
".git",
|
||||
".mypy_cache",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
future-annotations = true
|
||||
# Disable all rules on test cases by default:
|
||||
# test cases often deliberately contain code
|
||||
# that might not be considered idiomatic or modern.
|
||||
#
|
||||
# Note: some rules that are specifically useful to the test cases
|
||||
# are invoked via separate runs of ruff in pre-commit:
|
||||
# see our .pre-commit-config.yaml file for details
|
||||
exclude = ["**/test_cases/**/*.py"]
|
||||
# We still use flake8-pyi to check these (see .flake8 config file);
|
||||
# tell ruff not to flag these as e.g. "unused noqa comments"
|
||||
external = ["F821", "Y"]
|
||||
select = [
|
||||
"A", # flake8-builtins
|
||||
"ARG", # flake8-unused-arguments
|
||||
"B", # flake8-bugbear
|
||||
"C4", # flake8-comprehensions
|
||||
"D", # pydocstyle
|
||||
"DTZ", # flake8-datetimez
|
||||
"EXE", # flake8-executable
|
||||
"FA", # flake8-future-annotations
|
||||
"FBT", # flake8-boolean-trap
|
||||
"FLY", # flynt
|
||||
"I", # isort
|
||||
"N", # pep8-naming
|
||||
"PGH", # pygrep-hooks
|
||||
"PIE", # flake8-pie
|
||||
"PL", # Pylint
|
||||
"PTH", # flake8-use-pathlib
|
||||
"RSE", # flake8-raise
|
||||
"RUF", # Ruff-specific and unused-noqa
|
||||
"SLOT", # flake8-slots
|
||||
"T10", # flake8-debugger
|
||||
"TD", # flake8-todos
|
||||
"TRY", # tryceratops
|
||||
"UP", # pyupgrade
|
||||
"YTT", # flake8-2020
|
||||
# Flake8 base rules
|
||||
"E", # pycodestyle Error
|
||||
"F", # Pyflakes
|
||||
"W", # pycodestyle Warning
|
||||
# Only include flake8-annotations rules that are autofixable. Otherwise leave this to mypy+pyright
|
||||
"ANN2",
|
||||
# Most refurb rules are in preview and can be opinionated,
|
||||
# consider them individually as they come out of preview (last check: 0.13.1)
|
||||
"FURB105", # Unnecessary empty string passed to `print`
|
||||
"FURB116", # Replace `{function_name}` call with `{display}`
|
||||
"FURB122", # Use of `{}.write` in a for loop
|
||||
"FURB129", # Instead of calling `readlines()`, iterate over file object directly
|
||||
"FURB132", # Use `{suggestion}` instead of `check` and `remove`
|
||||
"FURB136", # Replace `if` expression with `{min_max}` call
|
||||
"FURB157", # Verbose expression in `Decimal` constructor
|
||||
"FURB162", # Unnecessary timezone replacement with zero offset
|
||||
"FURB166", # Use of `int` with explicit `base={base}` after removing prefix
|
||||
"FURB167", # Use of regular expression alias `re.{}`
|
||||
"FURB168", # Prefer `is` operator over `isinstance` to check if an object is `None`
|
||||
"FURB169", # Compare the identities of `{object}` and None instead of their respective types
|
||||
"FURB177", # Prefer `Path.cwd()` over `Path().resolve()` for current-directory lookups
|
||||
"FURB187", # Use of assignment of `reversed` on list `{name}`
|
||||
"FURB188", # Prefer `str.removeprefix()` over conditionally replacing with slice.
|
||||
# Used for lint.flake8-import-conventions.aliases
|
||||
"ICN001", # `{name}` should be imported as `{asname}`
|
||||
# PYI: only enable rules that have autofixes and that we always want to fix (even manually),
|
||||
# avoids duplicate # noqa with flake8-pyi
|
||||
"PYI009", # Empty body should contain `...`, not pass
|
||||
"PYI010", # Function body must contain only `...`
|
||||
"PYI012", # Class bodies must not contain `pass`
|
||||
"PYI013", # Non-empty class bodies must not contain `...`
|
||||
"PYI014", # Only simple default values allowed for arguments
|
||||
"PYI015", # Only simple default values allowed for assignments
|
||||
"PYI016", # Duplicate union member `{}`
|
||||
"PYI018", # Private `{type_var_like_kind}` `{type_var_like_name}` is never used
|
||||
"PYI019", # Methods like `{method_name}` should return `Self` instead of a custom `TypeVar`
|
||||
"PYI020", # Quoted annotations should not be included in stubs
|
||||
"PYI025", # Use `from collections.abc import Set as AbstractSet` to avoid confusion with the `set` builtin
|
||||
# "PYI026", Waiting for this mypy bug to be fixed: https://github.com/python/mypy/issues/16581
|
||||
"PYI030", # Multiple literal members in a union. Use a single literal, e.g. `Literal[{}]`
|
||||
"PYI032", # Prefer `object` to `Any` for the second parameter to `{method_name}`
|
||||
"PYI036", # Star-args in `{method_name}` should be annotated with `object`
|
||||
"PYI044", # `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
|
||||
"PYI055", # Multiple `type[T]` usages in a union. Combine them into one, e.g., `type[{union_str}]`.
|
||||
"PYI058", # Use `{return_type}` as the return value for simple `{method}` methods
|
||||
# "PYI059", # TODO: Add when dropping Python 3.9 support
|
||||
"PYI061", # Use `None` rather than `Literal[None]`
|
||||
"PYI062", # Duplicate literal member `{}`
|
||||
"PYI064", # `Final[Literal[{literal}]]` can be replaced with a bare Final
|
||||
# flake8-simplify, excluding rules that can reduce performance or readability due to long line formatting
|
||||
"SIM101", # Multiple `isinstance` calls for `{name}`, merge into a single call
|
||||
"SIM103", # Return the condition `{condition}` directly
|
||||
"SIM107", # Don't use return in `try-except` and `finally`
|
||||
"SIM109", # Use `{replacement}` instead of multiple equality comparisons
|
||||
"SIM112", # Use capitalized environment variable `{expected}` instead of `{actual}`
|
||||
"SIM113", # Use `enumerate()` for index variable `{index}` in `for` loop
|
||||
"SIM114", # Combine `if` branches using logical `or` operator
|
||||
"SIM115", # Use a context manager for opening files
|
||||
"SIM118", # Use key `{operator}` dict instead of key `{operator} dict.keys()`
|
||||
"SIM201", # Use `{left} != {right}` instead of not `{left} == {right}`
|
||||
"SIM202", # Use `{left} == {right}` instead of not `{left} != {right}`
|
||||
"SIM208", # Use `{expr}` instead of `not (not {expr})`
|
||||
"SIM210", # Remove unnecessary `True if ... else False`
|
||||
"SIM211", # Use `not ...` instead of `False if ... else True`
|
||||
"SIM212", # Use `{expr_else} if {expr_else} else {expr_body}` instead of `{expr_body} if not {expr_else} else {expr_else}`
|
||||
"SIM220", # Use `False` instead of `{name} and not {name}`
|
||||
"SIM221", # Use `True` instead of `{name} or not {name}`
|
||||
"SIM222", # Use `{expr}` instead of `{replaced}`
|
||||
"SIM223", # Use `{expr}` instead of `{replaced}`
|
||||
"SIM300", # Yoda condition detected
|
||||
"SIM401", # Use `{contents}` instead of an if block
|
||||
"SIM905", # Consider using a list literal instead of `str.{}`
|
||||
"SIM910", # Use `{expected}` instead of `{actual}` (dict-get-with-none-default)
|
||||
"SIM911", # Use `{expected}` instead of `{actual}` (zip-dict-keys-and-values)
|
||||
# Don't include TC rules that create a TYPE_CHECKING block or stringifies annotations
|
||||
"TC004", # Move import `{qualified_name}` out of type-checking block. Import is used for more than type hinting.
|
||||
"TC005", # Found empty type-checking block
|
||||
# "TC008", # TODO: Enable when out of preview
|
||||
"TC010", # Invalid string member in `X | Y`-style union type
|
||||
# Used for lint.flake8-import-conventions.aliases
|
||||
"TID251", # `{name}` is banned: {message}
|
||||
]
|
||||
extend-safe-fixes = [
|
||||
"UP036", # Remove unnecessary `sys.version_info` blocks
|
||||
]
|
||||
ignore = [
|
||||
###
|
||||
# TODO: Disabled temporarily, until Python 3.9 support is fully removed in
|
||||
# May 2026.
|
||||
###
|
||||
"UP035", # import from typing
|
||||
"UP036", # Remove unnecessary `sys.version_info` blocks
|
||||
###
|
||||
# Rules that can conflict with the formatter (Black)
|
||||
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
|
||||
###
|
||||
"E111", # indentation-with-invalid-multiple
|
||||
"E114", # indentation-with-invalid-multiple-comment
|
||||
"E117", # over-indented
|
||||
"W191", # tab-indentation
|
||||
###
|
||||
# Rules we don't want or don't agree with
|
||||
###
|
||||
# We're not a library, no need to document everything
|
||||
"D1", # Missing docstring in ...
|
||||
# Sometimes, an extra blank line is more readable
|
||||
"D202", # No blank lines allowed after function docstring
|
||||
# Doesn't support split "summary line"
|
||||
"D205", # 1 blank line required between summary line and description
|
||||
# Used for direct, non-subclass type comparison, for example: `type(val) is str`
|
||||
# see https://github.com/astral-sh/ruff/issues/6465
|
||||
"E721", # Do not compare types, use `isinstance()`
|
||||
# Highly opinionated, and it's often necessary to violate it
|
||||
"PLC0415", # `import` should be at the top-level of a file
|
||||
# Leave the size and complexity of tests to human interpretation
|
||||
"PLR09", # Too many ...
|
||||
# Too many magic number "2" that are preferable inline. https://github.com/astral-sh/ruff/issues/10009
|
||||
"PLR2004", # Magic value used in comparison, consider replacing `{value}` with a constant variable
|
||||
# Keep codeflow path separation explicit
|
||||
"PLR5501", # Use `elif` instead of `else` then `if`, to reduce indentation
|
||||
# Often just leads to redundant more verbose code when needing an actual str
|
||||
"PTH208", # Use `pathlib.Path.iterdir()` instead.
|
||||
# Allow FIXME
|
||||
"TD001", # Invalid TODO tag: `{tag}`
|
||||
# Git blame is sufficient
|
||||
"TD002", # Missing author in TODO;
|
||||
"TD003", # Missing issue link for this TODO
|
||||
# Mostly from scripts and tests, it's ok to have messages passed directly to exceptions
|
||||
"TRY003", # Avoid specifying long messages outside the exception class
|
||||
###
|
||||
# False-positives, but already checked by type-checkers
|
||||
###
|
||||
# Ruff doesn't support multi-file analysis yet: https://github.com/astral-sh/ruff/issues/5295
|
||||
"RUF013", # PEP 484 prohibits implicit `Optional`
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"*.pyi" = [
|
||||
# A lot of stubs are incomplete on purpose, and that's configured through pyright
|
||||
# Some ANN204 (special method) are autofixable in stubs, but not all.
|
||||
"ANN2", # Missing return type annotation for ...
|
||||
# Ruff 0.8.0 added sorting of __all__ and __slots_.
|
||||
# There is no consensus on whether we want to apply this to stubs, so keeping the status quo.
|
||||
# See https://github.com/python/typeshed/pull/13108
|
||||
"RUF022", # `__all__` is not sorted
|
||||
"RUF023", # `{}.__slots__` is not sorted
|
||||
###
|
||||
# Rules that are out of the control of stub authors:
|
||||
###
|
||||
# Names in stubs should match the implementation, even if it's ambiguous.
|
||||
# https://github.com/astral-sh/ruff/issues/15293
|
||||
"A", # flake8-builtins
|
||||
# Stubs can sometimes re-export entire modules.
|
||||
# Issues with using a star-imported name will be caught by type-checkers.
|
||||
"F403", # `from . import *` used; unable to detect undefined names
|
||||
"F405", # may be undefined, or defined from star imports
|
||||
# Most pep8-naming rules don't apply for third-party stubs like typeshed.
|
||||
# N811 to N814 could apply, but we often use them to disambiguate a name whilst making it look like a more common one
|
||||
"N8", # pep8-naming
|
||||
# Sometimes __slots__ really is a string at runtime
|
||||
"PLC0205", # Class `__slots__` should be a non-string iterable
|
||||
# Stubs are allowed to use private variables (pyright's reportPrivateUsage is also disabled)
|
||||
"PLC2701", # Private name import from external module
|
||||
# Names in stubs should match implementation
|
||||
"PLW0211", # First argument of a static method should not be named `{argument_name}`
|
||||
]
|
||||
"lib/ts_utils/**" = [
|
||||
# Doesn't affect stubs. The only re-exports we have should be in our local lib ts_utils
|
||||
"PLC0414", # Import alias does not rename original package
|
||||
]
|
||||
"*_pb2.pyi" = [
|
||||
# Special autogenerated typing --> typing_extensions aliases
|
||||
"ICN001", # `{name}` should be imported as `{asname}`
|
||||
# Leave the docstrings as-is, matching source
|
||||
"D", # pydocstyle
|
||||
# See comment on black's force-exclude config above
|
||||
"E501", # Line too long
|
||||
]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "pep257" # https://docs.astral.sh/ruff/settings/#lint_pydocstyle_convention
|
||||
|
||||
[tool.ruff.lint.flake8-import-conventions.aliases]
|
||||
# Prevent aliasing these, as it causes false-negatives for certain rules
|
||||
typing_extensions = "typing_extensions"
|
||||
typing = "typing"
|
||||
|
||||
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
||||
"tempfile.NamedTemporaryFile".msg = "Use `ts_util.util.NamedTemporaryFile` instead."
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
split-on-trailing-comma = false
|
||||
combine-as-imports = true
|
||||
extra-standard-library = [
|
||||
# Group these with stdlib
|
||||
"_typeshed",
|
||||
"typing_extensions",
|
||||
# Extra modules not recognized by Ruff
|
||||
# Added in Python 3.14
|
||||
"compression",
|
||||
]
|
||||
known-first-party = ["_utils", "ts_utils"]
|
||||
|
||||
[tool.typeshed]
|
||||
oldest-supported-python = "3.10"
|
||||
[tool.isort]
|
||||
multi_line_output = 3
|
||||
include_trailing_comma = true
|
||||
force_grid_wrap = 0
|
||||
use_parentheses = true
|
||||
combine_as_imports = true
|
||||
line_length = 130
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
|
||||
"typeshedPath": ".",
|
||||
"include": [
|
||||
"stdlib",
|
||||
"stubs",
|
||||
],
|
||||
"exclude": [
|
||||
// Stubs that don't work in all Python versions
|
||||
"stubs/seaborn",
|
||||
"stubs/shapely",
|
||||
"stubs/geopandas",
|
||||
// test cases use a custom config file
|
||||
"**/@tests/test_cases",
|
||||
],
|
||||
"typeCheckingMode": "strict",
|
||||
// Allowed in base settings for incomplete stubs, checked in stricter settings
|
||||
"reportIncompleteStub": "none",
|
||||
"reportMissingParameterType": "none",
|
||||
"reportUnknownMemberType": "none",
|
||||
"reportUnknownParameterType": "none",
|
||||
"reportUnknownVariableType": "none",
|
||||
// Extra strict settings
|
||||
"reportCallInDefaultInitializer": "error",
|
||||
"reportUnnecessaryTypeIgnoreComment": "error",
|
||||
// Leave "type: ignore" comments to mypy
|
||||
"enableTypeIgnoreComments": false,
|
||||
// No effect in stubs
|
||||
"reportMissingSuperCall": "none",
|
||||
"reportUninitializedInstanceVariable": "none",
|
||||
// Stubs are allowed to use private variables
|
||||
"reportPrivateUsage": "none",
|
||||
// Stubs don't need the actual modules to be installed
|
||||
"reportMissingModuleSource": "none",
|
||||
// Incompatible overrides and property type mismatches are out of typeshed's control
|
||||
// as they are inherited from the implementation.
|
||||
"reportIncompatibleMethodOverride": "none",
|
||||
"reportIncompatibleVariableOverride": "none",
|
||||
"reportPropertyTypeMismatch": "none",
|
||||
// Overlapping overloads are often necessary in a stub, meaning pyright's check
|
||||
// (which is stricter than mypy's; see mypy issue #10143 and #10157)
|
||||
// would cause many false positives and catch few bugs.
|
||||
"reportOverlappingOverload": "none",
|
||||
// The name of the self/cls parameter is out of typeshed's control.
|
||||
"reportSelfClsParameterName": "none",
|
||||
// Not actionable in typeshed
|
||||
"reportDeprecated": "none",
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
|
||||
"typeshedPath": ".",
|
||||
"include": [
|
||||
"lib",
|
||||
"scripts",
|
||||
"tests",
|
||||
],
|
||||
"extraPaths": [
|
||||
"lib",
|
||||
],
|
||||
"typeCheckingMode": "strict",
|
||||
// More of a lint. Unwanted for typeshed's own code.
|
||||
"reportImplicitStringConcatenation": "none",
|
||||
// Extra strict settings
|
||||
"reportMissingModuleSource": "error",
|
||||
"reportCallInDefaultInitializer": "error",
|
||||
"reportPropertyTypeMismatch": "error",
|
||||
"reportUninitializedInstanceVariable": "error",
|
||||
"reportUnnecessaryTypeIgnoreComment": "error",
|
||||
// Leave "type: ignore" comments to mypy
|
||||
"enableTypeIgnoreComments": false,
|
||||
// Too strict
|
||||
"reportMissingSuperCall": "none",
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
|
||||
"typeshedPath": ".",
|
||||
"include": [
|
||||
"stdlib",
|
||||
"stubs",
|
||||
],
|
||||
"exclude": [
|
||||
// test cases use a custom pyrightconfig file
|
||||
"**/@tests/test_cases",
|
||||
"stdlib/__main__.pyi",
|
||||
"stdlib/_operator.pyi",
|
||||
"stdlib/_tkinter.pyi",
|
||||
"stdlib/distutils/cmd.pyi",
|
||||
"stdlib/distutils/command",
|
||||
"stdlib/distutils/dist.pyi",
|
||||
"stdlib/encodings/__init__.pyi",
|
||||
"stdlib/lib2to3/fixes/*.pyi",
|
||||
"stdlib/numbers.pyi",
|
||||
"stdlib/operator.pyi",
|
||||
"stdlib/tkinter/__init__.pyi",
|
||||
"stdlib/tkinter/dialog.pyi",
|
||||
"stdlib/tkinter/filedialog.pyi",
|
||||
"stdlib/tkinter/scrolledtext.pyi",
|
||||
"stdlib/tkinter/tix.pyi",
|
||||
"stdlib/tkinter/ttk.pyi",
|
||||
"stubs/antlr4-python3-runtime",
|
||||
"stubs/auth0-python",
|
||||
"stubs/Authlib",
|
||||
"stubs/aws-xray-sdk",
|
||||
"stubs/behave",
|
||||
"stubs/boltons",
|
||||
"stubs/braintree",
|
||||
"stubs/cffi",
|
||||
"stubs/colorful",
|
||||
"stubs/dateparser",
|
||||
"stubs/defusedxml",
|
||||
"stubs/docker",
|
||||
"stubs/docutils",
|
||||
"stubs/Flask-SocketIO",
|
||||
"stubs/fpdf2",
|
||||
"stubs/gdb",
|
||||
"stubs/geopandas",
|
||||
"stubs/google-cloud-ndb",
|
||||
"stubs/grpcio-channelz/grpc_channelz/v1",
|
||||
"stubs/grpcio-health-checking/grpc_health/v1/health_pb2_grpc.pyi",
|
||||
"stubs/grpcio-reflection/grpc_reflection/v1alpha",
|
||||
"stubs/grpcio-status/grpc_status",
|
||||
"stubs/grpcio/grpc/__init__.pyi",
|
||||
"stubs/gunicorn/gunicorn/dirty",
|
||||
"stubs/hdbcli/hdbcli/dbapi.pyi",
|
||||
"stubs/html5lib",
|
||||
"stubs/httplib2",
|
||||
"stubs/hvac",
|
||||
"stubs/icalendar/icalendar/prop.pyi",
|
||||
"stubs/icalendar/icalendar/timezone/provider.pyi",
|
||||
"stubs/jsonschema",
|
||||
"stubs/jwcrypto",
|
||||
"stubs/ldap3",
|
||||
"stubs/m3u8/m3u8/model.pyi",
|
||||
"stubs/Markdown",
|
||||
"stubs/mock/mock/mock.pyi",
|
||||
"stubs/mysqlclient",
|
||||
"stubs/netaddr/netaddr/core.pyi",
|
||||
"stubs/netaddr/netaddr/ip/__init__.pyi",
|
||||
"stubs/netaddr/netaddr/ip/iana.pyi",
|
||||
"stubs/networkx",
|
||||
"stubs/oauthlib",
|
||||
"stubs/openpyxl",
|
||||
"stubs/opentracing/opentracing/span.pyi",
|
||||
"stubs/paramiko/paramiko/_winapi.pyi",
|
||||
"stubs/parsimonious/parsimonious/nodes.pyi",
|
||||
"stubs/peewee",
|
||||
"stubs/pexpect",
|
||||
"stubs/pika",
|
||||
"stubs/pony",
|
||||
"stubs/protobuf",
|
||||
"stubs/psutil/psutil/__init__.pyi",
|
||||
"stubs/psycopg2",
|
||||
"stubs/pyasn1",
|
||||
"stubs/pycurl",
|
||||
"stubs/Pygments",
|
||||
"stubs/PyMySQL",
|
||||
"stubs/python-jose",
|
||||
"stubs/pywin32",
|
||||
"stubs/PyYAML",
|
||||
"stubs/reportlab",
|
||||
"stubs/requests",
|
||||
"stubs/requests-oauthlib",
|
||||
"stubs/seaborn",
|
||||
"stubs/setuptools/setuptools",
|
||||
"stubs/shapely",
|
||||
"stubs/simple-websocket",
|
||||
"stubs/tensorflow",
|
||||
"stubs/tqdm",
|
||||
"stubs/vobject",
|
||||
"stubs/workalendar",
|
||||
"stubs/xmldiff",
|
||||
],
|
||||
"typeCheckingMode": "strict",
|
||||
// TODO: Complete incomplete stubs
|
||||
"reportIncompleteStub": "none",
|
||||
// Extra strict settings
|
||||
"reportCallInDefaultInitializer": "error",
|
||||
// implicit string concatenation is useful for long deprecation messages
|
||||
"reportImplicitStringConcatenation": "none",
|
||||
"reportUnnecessaryTypeIgnoreComment": "error",
|
||||
// Leave "type: ignore" comments to mypy
|
||||
"enableTypeIgnoreComments": false,
|
||||
// No effect in stubs
|
||||
"reportMissingSuperCall": "none",
|
||||
"reportUninitializedInstanceVariable": "none",
|
||||
// Stubs are allowed to use private variables
|
||||
"reportPrivateUsage": "none",
|
||||
// Stubs don't need the actual modules to be installed
|
||||
"reportMissingModuleSource": "none",
|
||||
// Incompatible overrides and property type mismatches are out of typeshed's control
|
||||
// as they are inherited from the implementation.
|
||||
"reportIncompatibleMethodOverride": "none",
|
||||
"reportIncompatibleVariableOverride": "none",
|
||||
"reportPropertyTypeMismatch": "none",
|
||||
// Overlapping overloads are often necessary in a stub, meaning pyright's check
|
||||
// (which is stricter than mypy's; see mypy issue #10143 and #10157)
|
||||
// would cause many false positives and catch few bugs.
|
||||
"reportOverlappingOverload": "none",
|
||||
// The name of the self/cls parameter is out of typeshed's control.
|
||||
"reportSelfClsParameterName": "none",
|
||||
// Not actionable in typeshed
|
||||
"reportDeprecated": "none",
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
|
||||
"typeshedPath": ".",
|
||||
"include": [
|
||||
"**/@tests/test_cases",
|
||||
],
|
||||
"typeCheckingMode": "strict",
|
||||
// Extra strict settings
|
||||
"reportImplicitStringConcatenation": "error",
|
||||
"reportUninitializedInstanceVariable": "error",
|
||||
"reportUnnecessaryTypeIgnoreComment": "error",
|
||||
// Using unspecific `type: ignore` comments in test_cases.
|
||||
// See https://github.com/python/typeshed/pull/8083
|
||||
"enableTypeIgnoreComments": true,
|
||||
// If a test case uses this anti-pattern, there's likely a reason and annoying to `type: ignore`.
|
||||
// Let Ruff flag it (B006)
|
||||
"reportCallInDefaultInitializer": "none",
|
||||
// Too strict and not needed for type testing
|
||||
"reportMissingSuperCall": "none",
|
||||
// Stubs are allowed to use private variables. We may want to test those.
|
||||
"reportPrivateUsage": "none",
|
||||
// Stubs don't need the actual modules to be installed
|
||||
"reportMissingModuleSource": "none",
|
||||
// Incompatible property type mismatches may be out of typeshed's control
|
||||
// when they are inherited from the implementation.
|
||||
"reportPropertyTypeMismatch": "none",
|
||||
// isinstance checks are still needed when validating inputs outside of typeshed's control
|
||||
"reportUnnecessaryIsInstance": "none",
|
||||
// The name of the self/cls parameter is out of typeshed's control.
|
||||
"reportSelfClsParameterName": "none",
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
git+https://github.com/python/mypy.git@master
|
||||
typed-ast>=1.0.4
|
||||
black==19.3b0
|
||||
flake8==3.7.8
|
||||
flake8-bugbear==19.8.0
|
||||
flake8-pyi==19.3.0
|
||||
isort==4.3.21
|
||||
pytype>=2019.10.17
|
||||
@@ -1,23 +0,0 @@
|
||||
# Type checkers that we test our stubs against. These should always
|
||||
# be pinned to a specific version to make failure reproducible.
|
||||
mypy==1.20.0
|
||||
pyright==1.1.408
|
||||
|
||||
# Libraries used by our various scripts.
|
||||
aiohttp==3.13.5
|
||||
grpcio-tools>=1.76.0 # For grpc_tools.protoc
|
||||
mypy-protobuf==5.0.0
|
||||
packaging==26.0
|
||||
pathspec>=1.0.3
|
||||
pre-commit
|
||||
# Required by create_baseline_stubs.py. Must match .pre-commit-config.yaml.
|
||||
ruff==0.15.8
|
||||
stubdefaulter==0.1.0
|
||||
termcolor>=2.3
|
||||
tomli==2.4.1; python_version < "3.11"
|
||||
tomlkit==0.14.0
|
||||
typing_extensions>=4.15.0rc1
|
||||
uv==0.11.6
|
||||
|
||||
# Utilities for typeshed infrastructure scripts.
|
||||
ts_utils @ file:lib
|
||||
@@ -1,255 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Script to generate unannotated baseline stubs using stubgen.
|
||||
|
||||
Basic usage:
|
||||
$ python3 scripts/create_baseline_stubs.py <project on PyPI>
|
||||
|
||||
Run with -h for more help.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.parse
|
||||
from http import HTTPStatus
|
||||
from importlib.metadata import distribution
|
||||
from pathlib import Path
|
||||
|
||||
import aiohttp
|
||||
import termcolor
|
||||
|
||||
from ts_utils.paths import PYRIGHT_CONFIG, STDLIB_PATH, STUBS_PATH
|
||||
|
||||
|
||||
def search_pip_freeze_output(project: str, output: str) -> tuple[str, str] | None:
|
||||
# Look for lines such as "typed-ast==1.4.2". '-' matches '_' and
|
||||
# '_' matches '-' in project name, so that "typed_ast" matches
|
||||
# "typed-ast", and vice versa.
|
||||
regex = "^(" + re.sub(r"[-_]", "[-_]", project) + ")==(.*)"
|
||||
m = re.search(regex, output, flags=re.IGNORECASE | re.MULTILINE)
|
||||
if not m:
|
||||
return None
|
||||
return m.group(1), m.group(2)
|
||||
|
||||
|
||||
def get_installed_package_info(project: str) -> tuple[str, str] | None:
|
||||
"""Find package information from pip freeze output.
|
||||
|
||||
Match project name somewhat fuzzily (case sensitive; '-' matches '_', and
|
||||
vice versa).
|
||||
|
||||
Return (normalized project name, installed version) if successful.
|
||||
"""
|
||||
# Not using "uv pip freeze" because if this is run from a global Python,
|
||||
# it'll mistakenly list the .venv's packages.
|
||||
r = subprocess.run(["pip", "freeze"], capture_output=True, text=True, check=True)
|
||||
return search_pip_freeze_output(project, r.stdout)
|
||||
|
||||
|
||||
def run_stubgen(package: str, output: Path) -> None:
|
||||
print(f"Running stubgen: stubgen -o {output} -p {package}")
|
||||
subprocess.run(["stubgen", "-o", output, "-p", package, "--export-less"], check=True)
|
||||
|
||||
|
||||
def run_stubdefaulter(stub_dir: Path) -> None:
|
||||
print(f"Running stubdefaulter: stubdefaulter --packages {stub_dir}")
|
||||
subprocess.run(["stubdefaulter", "--packages", stub_dir], check=False)
|
||||
|
||||
|
||||
def run_black(stub_dir: Path) -> None:
|
||||
print(f"Running Black: black {stub_dir}")
|
||||
subprocess.run(["pre-commit", "run", "black", "--files", *stub_dir.rglob("*.pyi")], check=False)
|
||||
|
||||
|
||||
def run_ruff(stub_dir: Path) -> None:
|
||||
print(f"Running Ruff: ruff check {stub_dir} --fix-only")
|
||||
subprocess.run([sys.executable, "-m", "ruff", "check", stub_dir, "--fix-only"], check=False)
|
||||
|
||||
|
||||
async def get_project_urls_from_pypi(project: str, session: aiohttp.ClientSession) -> dict[str, str]:
|
||||
pypi_root = f"https://pypi.org/pypi/{urllib.parse.quote(project)}"
|
||||
async with session.get(f"{pypi_root}/json") as response:
|
||||
if response.status != HTTPStatus.OK:
|
||||
return {}
|
||||
j: dict[str, dict[str, dict[str, str]]]
|
||||
j = await response.json()
|
||||
return j["info"].get("project_urls") or {}
|
||||
|
||||
|
||||
async def get_upstream_repo_url(project: str) -> str | None:
|
||||
# aiohttp is overkill here, but it would also just be silly
|
||||
# to have both requests and aiohttp in our requirements-tests.txt file.
|
||||
async with aiohttp.ClientSession() as session:
|
||||
project_urls = await get_project_urls_from_pypi(project, session)
|
||||
|
||||
if not project_urls:
|
||||
return None
|
||||
|
||||
# Order the project URLs so that we put the ones
|
||||
# that are most likely to point to the source code first
|
||||
urls_to_check: list[str] = []
|
||||
url_names_probably_pointing_to_source = ("Source", "Repository", "Homepage")
|
||||
for url_name in url_names_probably_pointing_to_source:
|
||||
if url := project_urls.get(url_name):
|
||||
urls_to_check.append(url)
|
||||
urls_to_check.extend(
|
||||
url for url_name, url in project_urls.items() if url_name not in url_names_probably_pointing_to_source
|
||||
)
|
||||
|
||||
for url_to_check in urls_to_check:
|
||||
# Remove `www.`; replace `http://` with `https://`
|
||||
url = re.sub(r"^(https?://)?(www\.)?", "https://", url_to_check)
|
||||
netloc = urllib.parse.urlparse(url).netloc
|
||||
if netloc not in {"gitlab.com", "github.com", "bitbucket.org", "foss.heptapod.net"}:
|
||||
continue
|
||||
# truncate to https://site.com/user/repo
|
||||
upstream_repo_url = "/".join(url.split("/")[:5])
|
||||
async with session.get(upstream_repo_url, allow_redirects=True) as response:
|
||||
if response.status != HTTPStatus.OK:
|
||||
continue
|
||||
# final url after redirects
|
||||
final_url = str(response.url)
|
||||
# normalize again (in case redirect added extra path)
|
||||
final_repo_url = "/".join(final_url.split("/")[:5])
|
||||
return final_repo_url
|
||||
return None
|
||||
|
||||
|
||||
def create_metadata(project: str, stub_dir: Path, version: str) -> None:
|
||||
"""Create a METADATA.toml file."""
|
||||
match = re.match(r"[0-9]+.[0-9]+", version)
|
||||
if match is None:
|
||||
sys.exit(f"Error: Cannot parse version number: {version}")
|
||||
filename = stub_dir / "METADATA.toml"
|
||||
version = match.group(0)
|
||||
if filename.exists():
|
||||
return
|
||||
metadata = f'version = "{version}.*"\n'
|
||||
upstream_repo_url = asyncio.run(get_upstream_repo_url(project))
|
||||
if upstream_repo_url is None:
|
||||
warning = (
|
||||
f"\nCould not find a URL pointing to the source code for {project!r}.\n"
|
||||
f"Please add it as `upstream-repository` to `stubs/{project}/METADATA.toml`, if possible!\n"
|
||||
)
|
||||
print(termcolor.colored(warning, "red"))
|
||||
else:
|
||||
metadata += f'upstream-repository = "{upstream_repo_url}"\n'
|
||||
print(f"Writing {filename}")
|
||||
filename.write_text(metadata, encoding="UTF-8")
|
||||
|
||||
|
||||
def add_pyright_exclusion(stub_dir: Path) -> None:
|
||||
"""Exclude stub_dir from strict pyright checks."""
|
||||
with PYRIGHT_CONFIG.open(encoding="UTF-8") as f:
|
||||
lines = f.readlines()
|
||||
i = 0
|
||||
while i < len(lines) and not lines[i].strip().startswith('"exclude": ['):
|
||||
i += 1
|
||||
assert i < len(lines), f"Error parsing {PYRIGHT_CONFIG}"
|
||||
while not lines[i].strip().startswith("]"):
|
||||
i += 1
|
||||
end = i
|
||||
|
||||
# We assume that all third-party excludes must be at the end of the list.
|
||||
# This helps with skipping special entries, such as "stubs/**/@tests/test_cases".
|
||||
while lines[i - 1].strip().startswith('"stubs/'):
|
||||
i -= 1
|
||||
start = i
|
||||
|
||||
before_third_party_excludes = lines[:start]
|
||||
third_party_excludes = lines[start:end]
|
||||
after_third_party_excludes = lines[end:]
|
||||
|
||||
last_line = third_party_excludes[-1].rstrip()
|
||||
if not last_line.endswith(","):
|
||||
last_line += ","
|
||||
third_party_excludes[-1] = last_line + "\n"
|
||||
|
||||
# Must use forward slash in the .json file
|
||||
line_to_add = f' "{stub_dir.as_posix()}",\n'
|
||||
|
||||
if line_to_add in third_party_excludes:
|
||||
print(f"{PYRIGHT_CONFIG} already up-to-date")
|
||||
return
|
||||
|
||||
third_party_excludes.append(line_to_add)
|
||||
third_party_excludes.sort(key=str.lower)
|
||||
|
||||
print(f"Updating {PYRIGHT_CONFIG}")
|
||||
with PYRIGHT_CONFIG.open("w", encoding="UTF-8") as f:
|
||||
f.writelines(before_third_party_excludes)
|
||||
f.writelines(third_party_excludes)
|
||||
f.writelines(after_third_party_excludes)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="""Generate baseline stubs automatically for an installed pip package
|
||||
using stubgen. Also run Black and Ruff. If the name of
|
||||
the project is different from the runtime Python package name, you may
|
||||
need to use --package (example: --package yaml PyYAML).""")
|
||||
parser.add_argument("project", help="name of PyPI project for which to generate stubs under stubs/")
|
||||
parser.add_argument("--package", help="generate stubs for this Python package (default is autodetected)")
|
||||
args = parser.parse_args()
|
||||
project = args.project
|
||||
package: str = args.package
|
||||
|
||||
if not re.match(r"[a-zA-Z0-9-_.]+$", project):
|
||||
sys.exit(f"Invalid character in project name: {project!r}")
|
||||
|
||||
if not package:
|
||||
package = project # default
|
||||
# Try to find which packages are provided by the project
|
||||
# Use default if that fails or if several packages are found
|
||||
#
|
||||
# The importlib.metadata module is used for projects whose name is different
|
||||
# from the runtime Python package name (example: PyYAML/yaml)
|
||||
dist = distribution(project).read_text("top_level.txt")
|
||||
if dist is not None:
|
||||
packages = [name for name in dist.split() if not name.startswith("_")]
|
||||
if len(packages) == 1:
|
||||
package = packages[0]
|
||||
print(f'Using detected package "{package}" for project "{project}"', file=sys.stderr)
|
||||
print("Suggestion: Try again with --package argument if that's not what you wanted", file=sys.stderr)
|
||||
|
||||
if not STUBS_PATH.is_dir() or not STDLIB_PATH.is_dir():
|
||||
sys.exit("Error: Current working directory must be the root of typeshed repository")
|
||||
|
||||
# Get normalized project name and version of installed package.
|
||||
info = get_installed_package_info(project)
|
||||
if info is None:
|
||||
print(f'Error: "{project}" is not installed', file=sys.stderr)
|
||||
print(file=sys.stderr)
|
||||
print(f"Suggestion: Run `{sys.executable} -m pip install {project}` and try again", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
project, version = info
|
||||
|
||||
stub_dir = STUBS_PATH / project
|
||||
package_dir = stub_dir / package
|
||||
if package_dir.exists():
|
||||
sys.exit(f"Error: {package_dir} already exists (delete it first)")
|
||||
|
||||
run_stubgen(package, stub_dir)
|
||||
run_stubdefaulter(stub_dir)
|
||||
|
||||
run_ruff(stub_dir)
|
||||
run_black(stub_dir)
|
||||
|
||||
create_metadata(project, stub_dir, version)
|
||||
|
||||
# Since the generated stubs won't have many type annotations, we
|
||||
# have to exclude them from strict pyright checks.
|
||||
add_pyright_exclusion(stub_dir)
|
||||
|
||||
print("\nDone!\n\nSuggested next steps:")
|
||||
print(f" 1. Manually review the generated stubs in {stub_dir}")
|
||||
print(" 2. Optionally run tests and autofixes (see tests/README.md for details)")
|
||||
print(" 3. Commit the changes on a new branch and create a typeshed PR (don't force-push!)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,15 +0,0 @@
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from ts_utils.requirements import get_external_stub_requirements
|
||||
|
||||
|
||||
def main() -> None:
|
||||
requirements = get_external_stub_requirements()
|
||||
# By forwarding arguments, we naturally allow non-venv (system installs)
|
||||
# by letting the script's user follow uv's own helpful hint of passing the `--system` flag.
|
||||
subprocess.check_call(["uv", "pip", "install", *sys.argv[1:], *[str(requirement) for requirement in requirements]])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from collections.abc import Iterable
|
||||
from http.client import HTTPResponse
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
from urllib.request import urlopen
|
||||
from zipfile import ZipFile
|
||||
|
||||
from mypy_protobuf.main import ( # type: ignore[import-untyped] # pyright: ignore[reportMissingTypeStubs]
|
||||
__version__ as mypy_protobuf__version__,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import StrOrBytesPath, StrPath
|
||||
|
||||
MYPY_PROTOBUF_VERSION = mypy_protobuf__version__
|
||||
|
||||
|
||||
def download_file(url: str, destination: Path) -> None:
|
||||
print(f"Downloading '{url}' to '{destination}'")
|
||||
resp: HTTPResponse
|
||||
with urlopen(url) as resp:
|
||||
destination.write_bytes(resp.read())
|
||||
|
||||
|
||||
def extract_archive(archive_path: StrPath, destination: StrPath) -> None:
|
||||
print(f"Extracting '{archive_path}' to '{destination}'")
|
||||
with ZipFile(archive_path) as file_in:
|
||||
file_in.extractall(destination)
|
||||
|
||||
|
||||
def run_protoc(
|
||||
proto_paths: Iterable[StrPath], mypy_out: StrPath, proto_globs: Iterable[str], cwd: StrOrBytesPath | None = None
|
||||
) -> str:
|
||||
"""TODO: Describe parameters and return."""
|
||||
protoc_version = (
|
||||
subprocess.run([sys.executable, "-m", "grpc_tools.protoc", "--version"], capture_output=True, check=False)
|
||||
.stdout.decode()
|
||||
.strip()
|
||||
)
|
||||
print()
|
||||
print(protoc_version)
|
||||
protoc_args = [
|
||||
*[f"--proto_path={proto_path}" for proto_path in proto_paths],
|
||||
"--mypy_out",
|
||||
f"relax_strict_optional_primitives:{mypy_out}",
|
||||
*proto_globs,
|
||||
]
|
||||
print("Running: protoc\n " + "\n ".join(protoc_args) + "\n")
|
||||
subprocess.run((sys.executable, "-m", "grpc_tools.protoc", *protoc_args), cwd=cwd, check=True)
|
||||
return protoc_version
|
||||
@@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generates the protobuf stubs for the given protobuf version using mypy-protobuf.
|
||||
Generally, new minor versions are a good time to update the stubs.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from _utils import MYPY_PROTOBUF_VERSION, download_file, extract_archive, run_protoc
|
||||
from ts_utils.metadata import read_metadata, update_metadata
|
||||
from ts_utils.paths import distribution_path
|
||||
|
||||
# PyPi version has an extra "major" number that the Git version doesn't have
|
||||
PACKAGE_VERSION = read_metadata("protobuf").version_spec.version[2:]
|
||||
|
||||
STUBS_FOLDER = distribution_path("protobuf").absolute()
|
||||
ARCHIVE_FILENAME = f"protobuf-{PACKAGE_VERSION}.zip"
|
||||
ARCHIVE_URL = f"https://github.com/protocolbuffers/protobuf/releases/download/v{PACKAGE_VERSION}/{ARCHIVE_FILENAME}"
|
||||
EXTRACTED_PACKAGE_DIR = f"protobuf-{PACKAGE_VERSION}"
|
||||
|
||||
VERSION_PATTERN = re.compile(r'def game_version\(\):\n return "(.+?)"')
|
||||
PROTO_FILE_PATTERN = re.compile(r'"//:(.*)_proto"')
|
||||
|
||||
|
||||
def extract_python_version(file_path: Path) -> str:
|
||||
"""Extract the Python version from https://github.com/protocolbuffers/protobuf/blob/main/version.json ."""
|
||||
with file_path.open() as file:
|
||||
data: dict[str, Any] = json.load(file)
|
||||
# The root key will be the protobuf source code version
|
||||
version = next(iter(data.values()))["languages"]["python"]
|
||||
assert isinstance(version, str)
|
||||
return version
|
||||
|
||||
|
||||
def extract_proto_file_paths(temp_dir: Path) -> list[str]:
|
||||
"""
|
||||
Roughly reproduce the subset of .proto files on the public interface
|
||||
as described in py_proto_library calls in
|
||||
https://github.com/protocolbuffers/protobuf/blob/main/python/dist/BUILD.bazel .
|
||||
"""
|
||||
with (temp_dir / EXTRACTED_PACKAGE_DIR / "python" / "dist" / "BUILD.bazel").open() as file:
|
||||
matched_lines = filter(None, (re.search(PROTO_FILE_PATTERN, line) for line in file))
|
||||
proto_files = [
|
||||
EXTRACTED_PACKAGE_DIR + "/src/google/protobuf/" + match.group(1).replace("compiler_", "compiler/") + ".proto"
|
||||
for match in matched_lines
|
||||
]
|
||||
return proto_files
|
||||
|
||||
|
||||
def main() -> None:
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
# Fetch s2clientprotocol (which contains all the .proto files)
|
||||
archive_path = temp_dir / ARCHIVE_FILENAME
|
||||
download_file(ARCHIVE_URL, archive_path)
|
||||
extract_archive(archive_path, temp_dir)
|
||||
|
||||
# Remove existing pyi
|
||||
for old_stub in STUBS_FOLDER.rglob("*_pb2.pyi"):
|
||||
old_stub.unlink()
|
||||
|
||||
protoc_version = run_protoc(
|
||||
proto_paths=(f"{EXTRACTED_PACKAGE_DIR}/src",),
|
||||
mypy_out=STUBS_FOLDER,
|
||||
proto_globs=extract_proto_file_paths(temp_dir),
|
||||
cwd=temp_dir,
|
||||
)
|
||||
|
||||
python_protobuf_version = extract_python_version(temp_dir / EXTRACTED_PACKAGE_DIR / "version.json")
|
||||
|
||||
# Cleanup after ourselves, this is a temp dir, but it can still grow fast if run multiple times
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
update_metadata(
|
||||
"protobuf",
|
||||
extra_description=f"""Partially generated using \
|
||||
[mypy-protobuf=={MYPY_PROTOBUF_VERSION}](https://github.com/nipunn1313/mypy-protobuf/tree/v{MYPY_PROTOBUF_VERSION}) \
|
||||
and {protoc_version} on \
|
||||
[protobuf v{PACKAGE_VERSION}](https://github.com/protocolbuffers/protobuf/releases/tag/v{PACKAGE_VERSION}) \
|
||||
(python `protobuf=={python_protobuf_version}`).""",
|
||||
)
|
||||
print("Updated protobuf/METADATA.toml")
|
||||
|
||||
# Run pre-commit to cleanup the stubs
|
||||
subprocess.run((sys.executable, "-m", "pre_commit", "run", "--files", *STUBS_FOLDER.rglob("*_pb2.pyi")), check=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generates the protobuf stubs for the given s2clientprotocol version using mypy-protobuf.
|
||||
Generally, new minor versions are a good time to update the stubs.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from _utils import MYPY_PROTOBUF_VERSION, download_file, extract_archive, run_protoc
|
||||
from ts_utils.metadata import update_metadata
|
||||
from ts_utils.paths import distribution_path
|
||||
|
||||
# Whenever you update PACKAGE_VERSION here, version should be updated
|
||||
# in stubs/s2clientprotocol/METADATA.toml and vice-versa.
|
||||
PACKAGE_VERSION = "c04df4adbe274858a4eb8417175ee32ad02fd609"
|
||||
|
||||
STUBS_FOLDER = distribution_path("s2clientprotocol").absolute()
|
||||
ARCHIVE_FILENAME = f"{PACKAGE_VERSION}.zip"
|
||||
ARCHIVE_URL = f"https://github.com/Blizzard/s2client-proto/archive/{ARCHIVE_FILENAME}"
|
||||
EXTRACTED_PACKAGE_DIR = f"s2client-proto-{PACKAGE_VERSION}"
|
||||
|
||||
VERSION_PATTERN = re.compile(r'def game_version\(\):\n return "(.+?)"')
|
||||
|
||||
|
||||
def extract_python_version(file_path: Path) -> str:
|
||||
"""Extract Python version from s2clientprotocol's build file."""
|
||||
match = re.search(VERSION_PATTERN, file_path.read_text())
|
||||
assert match
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
# Fetch s2clientprotocol (which contains all the .proto files)
|
||||
archive_path = temp_dir / ARCHIVE_FILENAME
|
||||
download_file(ARCHIVE_URL, archive_path)
|
||||
extract_archive(archive_path, temp_dir)
|
||||
|
||||
# Remove existing pyi
|
||||
for old_stub in STUBS_FOLDER.rglob("*_pb2.pyi"):
|
||||
old_stub.unlink()
|
||||
|
||||
protoc_version = run_protoc(
|
||||
proto_paths=(EXTRACTED_PACKAGE_DIR,),
|
||||
mypy_out=STUBS_FOLDER,
|
||||
proto_globs=(f"{EXTRACTED_PACKAGE_DIR}/s2clientprotocol/*.proto",),
|
||||
cwd=temp_dir,
|
||||
)
|
||||
|
||||
python_s2_client_proto_version = extract_python_version(temp_dir / EXTRACTED_PACKAGE_DIR / "s2clientprotocol" / "build.py")
|
||||
|
||||
# Cleanup after ourselves, this is a temp dir, but it can still grow fast if run multiple times
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
update_metadata(
|
||||
"s2clientprotocol",
|
||||
extra_description=f"""Partially generated using \
|
||||
[mypy-protobuf=={MYPY_PROTOBUF_VERSION}](https://github.com/nipunn1313/mypy-protobuf/tree/v{MYPY_PROTOBUF_VERSION}) \
|
||||
and {protoc_version} on \
|
||||
[s2client-proto {python_s2_client_proto_version}](https://github.com/Blizzard/s2client-proto/tree/{PACKAGE_VERSION}).""",
|
||||
)
|
||||
print("Updated s2clientprotocol/METADATA.toml")
|
||||
|
||||
# Run pre-commit to cleanup the stubs
|
||||
subprocess.run((sys.executable, "-m", "pre_commit", "run", "--files", *STUBS_FOLDER.rglob("*_pb2.pyi")), check=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,141 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generates the protobuf stubs for the given tensorflow version using mypy-protobuf.
|
||||
Generally, new minor versions are a good time to update the stubs.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from _utils import MYPY_PROTOBUF_VERSION, download_file, extract_archive, run_protoc
|
||||
from ts_utils.metadata import read_metadata, update_metadata
|
||||
from ts_utils.paths import distribution_path
|
||||
|
||||
PACKAGE_VERSION = read_metadata("tensorflow").version_spec.version
|
||||
|
||||
STUBS_FOLDER = distribution_path("tensorflow").absolute()
|
||||
ARCHIVE_FILENAME = f"v{PACKAGE_VERSION}.zip"
|
||||
ARCHIVE_URL = f"https://github.com/tensorflow/tensorflow/archive/refs/tags/{ARCHIVE_FILENAME}"
|
||||
EXTRACTED_PACKAGE_DIR = f"tensorflow-{PACKAGE_VERSION}"
|
||||
|
||||
PROTOS_TO_REMOVE = (
|
||||
"compiler/xla/autotune_results_pb2.pyi",
|
||||
"compiler/xla/autotuning_pb2.pyi",
|
||||
"compiler/xla/service/buffer_assignment_pb2.pyi",
|
||||
"compiler/xla/service/hlo_execution_profile_data_pb2.pyi",
|
||||
"core/protobuf/autotuning_pb2.pyi",
|
||||
"core/protobuf/conv_autotuning_pb2.pyi",
|
||||
"core/protobuf/critical_section_pb2.pyi",
|
||||
"core/protobuf/eager_service_pb2.pyi",
|
||||
"core/protobuf/master_pb2.pyi",
|
||||
"core/protobuf/master_service_pb2.pyi",
|
||||
"core/protobuf/replay_log_pb2.pyi",
|
||||
"core/protobuf/tpu/compile_metadata_pb2.pyi",
|
||||
"core/protobuf/worker_pb2.pyi",
|
||||
"core/protobuf/worker_service_pb2.pyi",
|
||||
"core/util/example_proto_fast_parsing_test_pb2.pyi",
|
||||
)
|
||||
"""
|
||||
These protos exist in a folder with protos used in python,
|
||||
but are not included in the python wheel.
|
||||
They are likely only used for other language builds.
|
||||
stubtest was used to identify them by looking for ModuleNotFoundError.
|
||||
(comment out ".*_pb2.*" from the allowlist)
|
||||
"""
|
||||
|
||||
TSL_IMPORT_PATTERN = re.compile(r"(\[|\s)tsl\.")
|
||||
XLA_IMPORT_PATTERN = re.compile(r"(\[|\s)xla\.")
|
||||
|
||||
|
||||
def move_tree(source: Path, destination: Path) -> None:
|
||||
"""Move directory and merge if destination already exists.
|
||||
|
||||
Can't use shutil.move because it can't merge existing directories.
|
||||
"""
|
||||
print(f"Moving '{source}' to '{destination}'")
|
||||
shutil.copytree(source, destination, dirs_exist_ok=True)
|
||||
shutil.rmtree(source)
|
||||
|
||||
|
||||
def post_creation() -> None:
|
||||
"""Move third-party and fix imports."""
|
||||
print()
|
||||
move_tree(STUBS_FOLDER / "tsl", STUBS_FOLDER / "tensorflow" / "tsl")
|
||||
move_tree(STUBS_FOLDER / "xla", STUBS_FOLDER / "tensorflow" / "compiler" / "xla")
|
||||
|
||||
for path in STUBS_FOLDER.rglob("*_pb2.pyi"):
|
||||
print(f"Fixing imports in '{path}'")
|
||||
filedata = path.read_text(encoding="utf-8")
|
||||
|
||||
# Replace the target string
|
||||
filedata = re.sub(TSL_IMPORT_PATTERN, "\\1tensorflow.tsl.", filedata)
|
||||
filedata = re.sub(XLA_IMPORT_PATTERN, "\\1tensorflow.compiler.xla.", filedata)
|
||||
|
||||
# Write the file out again
|
||||
path.write_text(filedata, encoding="utf-8")
|
||||
|
||||
print()
|
||||
for to_remove in PROTOS_TO_REMOVE:
|
||||
file_path = STUBS_FOLDER / "tensorflow" / to_remove
|
||||
file_path.unlink()
|
||||
print(f"Removed '{file_path}'")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
# Fetch tensorflow (which contains all the .proto files)
|
||||
archive_path = temp_dir / ARCHIVE_FILENAME
|
||||
download_file(ARCHIVE_URL, archive_path)
|
||||
extract_archive(archive_path, temp_dir)
|
||||
|
||||
# Remove existing pyi
|
||||
for old_stub in STUBS_FOLDER.rglob("*_pb2.pyi"):
|
||||
old_stub.unlink()
|
||||
|
||||
protoc_version = run_protoc(
|
||||
proto_paths=(
|
||||
f"{EXTRACTED_PACKAGE_DIR}/third_party/xla/third_party/tsl",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/third_party/xla",
|
||||
f"{EXTRACTED_PACKAGE_DIR}",
|
||||
),
|
||||
mypy_out=STUBS_FOLDER,
|
||||
proto_globs=(
|
||||
f"{EXTRACTED_PACKAGE_DIR}/third_party/xla/xla/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/third_party/xla/xla/service/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/third_party/xla/xla/tsl/protobuf/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/tensorflow/core/example/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/tensorflow/core/framework/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/tensorflow/core/protobuf/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/tensorflow/core/protobuf/tpu/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/tensorflow/core/util/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/tensorflow/python/keras/protobuf/*.proto",
|
||||
f"{EXTRACTED_PACKAGE_DIR}/third_party/xla/third_party/tsl/tsl/protobuf/*.proto",
|
||||
),
|
||||
cwd=temp_dir,
|
||||
)
|
||||
|
||||
# Cleanup after ourselves, this is a temp dir, but it can still grow fast if run multiple times
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
post_creation()
|
||||
|
||||
update_metadata(
|
||||
"tensorflow",
|
||||
extra_description=f"""Partially generated using \
|
||||
[mypy-protobuf=={MYPY_PROTOBUF_VERSION}](https://github.com/nipunn1313/mypy-protobuf/tree/v{MYPY_PROTOBUF_VERSION}) \
|
||||
and {protoc_version} on `tensorflow=={PACKAGE_VERSION}`.""",
|
||||
)
|
||||
print("Updated tensorflow/METADATA.toml")
|
||||
|
||||
# Run pre-commit to cleanup the stubs
|
||||
subprocess.run((sys.executable, "-m", "pre_commit", "run", "--files", *STUBS_FOLDER.rglob("*_pb2.pyi")), check=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,47 @@
|
||||
# Stubs for BaseHTTPServer (Python 2.7)
|
||||
|
||||
from typing import Any, BinaryIO, Callable, Mapping, Optional, Tuple, Union
|
||||
import SocketServer
|
||||
import mimetools
|
||||
|
||||
class HTTPServer(SocketServer.TCPServer):
|
||||
server_name: str
|
||||
server_port: int
|
||||
def __init__(self, server_address: Tuple[str, int],
|
||||
RequestHandlerClass: Callable[..., BaseHTTPRequestHandler]) -> None: ...
|
||||
|
||||
class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
|
||||
client_address: Tuple[str, int]
|
||||
server: SocketServer.BaseServer
|
||||
close_connection: bool
|
||||
command: str
|
||||
path: str
|
||||
request_version: str
|
||||
headers: mimetools.Message
|
||||
rfile: BinaryIO
|
||||
wfile: BinaryIO
|
||||
server_version: str
|
||||
sys_version: str
|
||||
error_message_format: str
|
||||
error_content_type: str
|
||||
protocol_version: str
|
||||
MessageClass: type
|
||||
responses: Mapping[int, Tuple[str, str]]
|
||||
def __init__(self, request: bytes, client_address: Tuple[str, int],
|
||||
server: SocketServer.BaseServer) -> None: ...
|
||||
def handle(self) -> None: ...
|
||||
def handle_one_request(self) -> None: ...
|
||||
def send_error(self, code: int, message: Optional[str] = ...) -> None: ...
|
||||
def send_response(self, code: int,
|
||||
message: Optional[str] = ...) -> None: ...
|
||||
def send_header(self, keyword: str, value: str) -> None: ...
|
||||
def end_headers(self) -> None: ...
|
||||
def flush_headers(self) -> None: ...
|
||||
def log_request(self, code: Union[int, str] = ...,
|
||||
size: Union[int, str] = ...) -> None: ...
|
||||
def log_error(self, format: str, *args: Any) -> None: ...
|
||||
def log_message(self, format: str, *args: Any) -> None: ...
|
||||
def version_string(self) -> str: ...
|
||||
def date_time_string(self, timestamp: Optional[int] = ...) -> str: ...
|
||||
def log_date_time_string(self) -> str: ...
|
||||
def address_string(self) -> str: ...
|
||||
@@ -0,0 +1,8 @@
|
||||
# Stubs for CGIHTTPServer (Python 2.7)
|
||||
|
||||
from typing import List
|
||||
import SimpleHTTPServer
|
||||
|
||||
class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
cgi_directories: List[str]
|
||||
def do_POST(self) -> None: ...
|
||||
@@ -0,0 +1,99 @@
|
||||
from typing import Any, IO, Sequence, Tuple, Union, List, Dict, Protocol, Optional
|
||||
|
||||
DEFAULTSECT: str
|
||||
MAX_INTERPOLATION_DEPTH: int
|
||||
|
||||
class Error(Exception):
|
||||
message: Any
|
||||
def __init__(self, msg: str = ...) -> None: ...
|
||||
def _get_message(self) -> None: ...
|
||||
def _set_message(self, value: str) -> None: ...
|
||||
def __repr__(self) -> str: ...
|
||||
def __str__(self) -> str: ...
|
||||
|
||||
class NoSectionError(Error):
|
||||
section: str
|
||||
def __init__(self, section: str) -> None: ...
|
||||
|
||||
class DuplicateSectionError(Error):
|
||||
section: str
|
||||
def __init__(self, section: str) -> None: ...
|
||||
|
||||
class NoOptionError(Error):
|
||||
section: str
|
||||
option: str
|
||||
def __init__(self, option: str, section: str) -> None: ...
|
||||
|
||||
class InterpolationError(Error):
|
||||
section: str
|
||||
option: str
|
||||
msg: str
|
||||
def __init__(self, option: str, section: str, msg: str) -> None: ...
|
||||
|
||||
class InterpolationMissingOptionError(InterpolationError):
|
||||
reference: str
|
||||
def __init__(self, option: str, section: str, rawval: str, reference: str) -> None: ...
|
||||
|
||||
class InterpolationSyntaxError(InterpolationError): ...
|
||||
|
||||
class InterpolationDepthError(InterpolationError):
|
||||
def __init__(self, option: str, section: str, rawval: str) -> None: ...
|
||||
|
||||
class ParsingError(Error):
|
||||
filename: str
|
||||
errors: List[Tuple[Any, Any]]
|
||||
def __init__(self, filename: str) -> None: ...
|
||||
def append(self, lineno: Any, line: Any) -> None: ...
|
||||
|
||||
class MissingSectionHeaderError(ParsingError):
|
||||
lineno: Any
|
||||
line: Any
|
||||
def __init__(self, filename: str, lineno: Any, line: Any) -> None: ...
|
||||
|
||||
class _Readable(Protocol):
|
||||
def readline(self) -> str: ...
|
||||
|
||||
class RawConfigParser:
|
||||
_dict: Any
|
||||
_sections: Dict[Any, Any]
|
||||
_defaults: Dict[Any, Any]
|
||||
_optcre: Any
|
||||
SECTCRE: Any
|
||||
OPTCRE: Any
|
||||
OPTCRE_NV: Any
|
||||
def __init__(self, defaults: Dict[Any, Any] = ..., dict_type: Any = ..., allow_no_value: bool = ...) -> None: ...
|
||||
def defaults(self) -> Dict[Any, Any]: ...
|
||||
def sections(self) -> List[str]: ...
|
||||
def add_section(self, section: str) -> None: ...
|
||||
def has_section(self, section: str) -> bool: ...
|
||||
def options(self, section: str) -> List[str]: ...
|
||||
def read(self, filenames: Union[str, Sequence[str]]) -> List[str]: ...
|
||||
def readfp(self, fp: _Readable, filename: str = ...) -> None: ...
|
||||
def get(self, section: str, option: str) -> str: ...
|
||||
def items(self, section: str) -> List[Tuple[Any, Any]]: ...
|
||||
def _get(self, section: str, conv: type, option: str) -> Any: ...
|
||||
def getint(self, section: str, option: str) -> int: ...
|
||||
def getfloat(self, section: str, option: str) -> float: ...
|
||||
_boolean_states: Dict[str, bool]
|
||||
def getboolean(self, section: str, option: str) -> bool: ...
|
||||
def optionxform(self, optionstr: str) -> str: ...
|
||||
def has_option(self, section: str, option: str) -> bool: ...
|
||||
def set(self, section: str, option: str, value: Any = ...) -> None: ...
|
||||
def write(self, fp: IO[str]) -> None: ...
|
||||
def remove_option(self, section: str, option: Any) -> bool: ...
|
||||
def remove_section(self, section: str) -> bool: ...
|
||||
def _read(self, fp: IO[str], fpname: str) -> None: ...
|
||||
|
||||
class ConfigParser(RawConfigParser):
|
||||
_KEYCRE: Any
|
||||
def get(self, section: str, option: str, raw: bool = ..., vars: Optional[Dict[Any, Any]] = ...) -> Any: ...
|
||||
def items(self, section: str, raw: bool = ..., vars: Optional[Dict[Any, Any]] = ...) -> List[Tuple[str, Any]]: ...
|
||||
def _interpolate(self, section: str, option: str, rawval: Any, vars: Any) -> str: ...
|
||||
def _interpolation_replace(self, match: Any) -> str: ...
|
||||
|
||||
class SafeConfigParser(ConfigParser):
|
||||
_interpvar_re: Any
|
||||
def _interpolate(self, section: str, option: str, rawval: Any, vars: Any) -> str: ...
|
||||
def _interpolate_some(
|
||||
self, option: str, accum: List[Any], rest: str, section: str, map: Dict[Any, Any], depth: int,
|
||||
) -> None: ...
|
||||
@@ -0,0 +1,40 @@
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
class CookieError(Exception): ...
|
||||
|
||||
class Morsel(Dict[Any, Any]):
|
||||
key: Any
|
||||
def __init__(self): ...
|
||||
def __setitem__(self, K, V): ...
|
||||
def isReservedKey(self, K): ...
|
||||
value: Any
|
||||
coded_value: Any
|
||||
def set(self, key, val, coded_val, LegalChars=..., idmap=..., translate=...): ...
|
||||
def output(self, attrs: Optional[Any] = ..., header=...): ...
|
||||
def js_output(self, attrs: Optional[Any] = ...): ...
|
||||
def OutputString(self, attrs: Optional[Any] = ...): ...
|
||||
|
||||
class BaseCookie(Dict[Any, Any]):
|
||||
def value_decode(self, val): ...
|
||||
def value_encode(self, val): ...
|
||||
def __init__(self, input: Optional[Any] = ...): ...
|
||||
def __setitem__(self, key, value): ...
|
||||
def output(self, attrs: Optional[Any] = ..., header=..., sep=...): ...
|
||||
def js_output(self, attrs: Optional[Any] = ...): ...
|
||||
def load(self, rawdata): ...
|
||||
|
||||
class SimpleCookie(BaseCookie):
|
||||
def value_decode(self, val): ...
|
||||
def value_encode(self, val): ...
|
||||
|
||||
class SerialCookie(BaseCookie):
|
||||
def __init__(self, input: Optional[Any] = ...): ...
|
||||
def value_decode(self, val): ...
|
||||
def value_encode(self, val): ...
|
||||
|
||||
class SmartCookie(BaseCookie):
|
||||
def __init__(self, input: Optional[Any] = ...): ...
|
||||
def value_decode(self, val): ...
|
||||
def value_encode(self, val): ...
|
||||
|
||||
Cookie: Any
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import List, Tuple, AnyStr
|
||||
from markupbase import ParserBase
|
||||
|
||||
class HTMLParser(ParserBase):
|
||||
def __init__(self) -> None: ...
|
||||
def feed(self, feed: AnyStr) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def reset(self) -> None: ...
|
||||
|
||||
def get_starttag_text(self) -> AnyStr: ...
|
||||
def set_cdata_mode(self, AnyStr) -> None: ...
|
||||
def clear_cdata_mode(self) -> None: ...
|
||||
|
||||
def handle_startendtag(self, tag: AnyStr, attrs: List[Tuple[AnyStr, AnyStr]]): ...
|
||||
def handle_starttag(self, tag: AnyStr, attrs: List[Tuple[AnyStr, AnyStr]]): ...
|
||||
def handle_endtag(self, tag: AnyStr): ...
|
||||
def handle_charref(self, name: AnyStr): ...
|
||||
def handle_entityref(self, name: AnyStr): ...
|
||||
def handle_data(self, data: AnyStr): ...
|
||||
def handle_comment(self, data: AnyStr): ...
|
||||
def handle_decl(self, decl: AnyStr): ...
|
||||
def handle_pi(self, data: AnyStr): ...
|
||||
|
||||
def unknown_decl(self, data: AnyStr): ...
|
||||
|
||||
def unescape(self, s: AnyStr) -> AnyStr: ...
|
||||
|
||||
class HTMLParseError(Exception):
|
||||
msg: str
|
||||
lineno: int
|
||||
offset: int
|
||||
@@ -0,0 +1,31 @@
|
||||
# Stubs for Queue (Python 2)
|
||||
|
||||
from collections import deque
|
||||
from typing import Any, Deque, TypeVar, Generic, Optional
|
||||
|
||||
_T = TypeVar('_T')
|
||||
|
||||
class Empty(Exception): ...
|
||||
class Full(Exception): ...
|
||||
|
||||
class Queue(Generic[_T]):
|
||||
maxsize: Any
|
||||
mutex: Any
|
||||
not_empty: Any
|
||||
not_full: Any
|
||||
all_tasks_done: Any
|
||||
unfinished_tasks: Any
|
||||
queue: Deque[Any] # undocumented
|
||||
def __init__(self, maxsize: int = ...) -> None: ...
|
||||
def task_done(self) -> None: ...
|
||||
def join(self) -> None: ...
|
||||
def qsize(self) -> int: ...
|
||||
def empty(self) -> bool: ...
|
||||
def full(self) -> bool: ...
|
||||
def put(self, item: _T, block: bool = ..., timeout: Optional[float] = ...) -> None: ...
|
||||
def put_nowait(self, item: _T) -> None: ...
|
||||
def get(self, block: bool = ..., timeout: Optional[float] = ...) -> _T: ...
|
||||
def get_nowait(self) -> _T: ...
|
||||
|
||||
class PriorityQueue(Queue[_T]): ...
|
||||
class LifoQueue(Queue[_T]): ...
|
||||
@@ -0,0 +1,16 @@
|
||||
# Stubs for SimpleHTTPServer (Python 2)
|
||||
|
||||
from typing import Any, AnyStr, IO, Mapping, Optional, Union
|
||||
import BaseHTTPServer
|
||||
from StringIO import StringIO
|
||||
|
||||
class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
server_version: str
|
||||
def do_GET(self) -> None: ...
|
||||
def do_HEAD(self) -> None: ...
|
||||
def send_head(self) -> Optional[IO[str]]: ...
|
||||
def list_directory(self, path: Union[str, unicode]) -> Optional[StringIO[Any]]: ...
|
||||
def translate_path(self, path: AnyStr) -> AnyStr: ...
|
||||
def copyfile(self, source: IO[AnyStr], outputfile: IO[AnyStr]): ...
|
||||
def guess_type(self, path: Union[str, unicode]) -> str: ...
|
||||
extensions_map: Mapping[str, str]
|
||||
@@ -0,0 +1,99 @@
|
||||
# NB: SocketServer.pyi and socketserver.pyi must remain consistent!
|
||||
# Stubs for socketserver
|
||||
|
||||
from typing import Any, BinaryIO, Callable, Optional, Tuple, Type, Text, Union
|
||||
from socket import SocketType
|
||||
import sys
|
||||
import types
|
||||
|
||||
class BaseServer:
|
||||
address_family: int
|
||||
RequestHandlerClass: Callable[..., BaseRequestHandler]
|
||||
server_address: Tuple[str, int]
|
||||
socket: SocketType
|
||||
allow_reuse_address: bool
|
||||
request_queue_size: int
|
||||
socket_type: int
|
||||
timeout: Optional[float]
|
||||
def __init__(self, server_address: Any,
|
||||
RequestHandlerClass: Callable[..., BaseRequestHandler]) -> None: ...
|
||||
def fileno(self) -> int: ...
|
||||
def handle_request(self) -> None: ...
|
||||
def serve_forever(self, poll_interval: float = ...) -> None: ...
|
||||
def shutdown(self) -> None: ...
|
||||
def server_close(self) -> None: ...
|
||||
def finish_request(self, request: bytes,
|
||||
client_address: Tuple[str, int]) -> None: ...
|
||||
def get_request(self) -> None: ...
|
||||
def handle_error(self, request: bytes,
|
||||
client_address: Tuple[str, int]) -> None: ...
|
||||
def handle_timeout(self) -> None: ...
|
||||
def process_request(self, request: bytes,
|
||||
client_address: Tuple[str, int]) -> None: ...
|
||||
def server_activate(self) -> None: ...
|
||||
def server_bind(self) -> None: ...
|
||||
def verify_request(self, request: bytes,
|
||||
client_address: Tuple[str, int]) -> bool: ...
|
||||
if sys.version_info >= (3, 6):
|
||||
def __enter__(self) -> BaseServer: ...
|
||||
def __exit__(self, exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[types.TracebackType]) -> None: ...
|
||||
if sys.version_info >= (3, 3):
|
||||
def service_actions(self) -> None: ...
|
||||
|
||||
class TCPServer(BaseServer):
|
||||
def __init__(self, server_address: Tuple[str, int],
|
||||
RequestHandlerClass: Callable[..., BaseRequestHandler],
|
||||
bind_and_activate: bool = ...) -> None: ...
|
||||
|
||||
class UDPServer(BaseServer):
|
||||
def __init__(self, server_address: Tuple[str, int],
|
||||
RequestHandlerClass: Callable[..., BaseRequestHandler],
|
||||
bind_and_activate: bool = ...) -> None: ...
|
||||
|
||||
if sys.platform != 'win32':
|
||||
class UnixStreamServer(BaseServer):
|
||||
def __init__(self, server_address: Union[Text, bytes],
|
||||
RequestHandlerClass: Callable[..., BaseRequestHandler],
|
||||
bind_and_activate: bool = ...) -> None: ...
|
||||
|
||||
class UnixDatagramServer(BaseServer):
|
||||
def __init__(self, server_address: Union[Text, bytes],
|
||||
RequestHandlerClass: Callable[..., BaseRequestHandler],
|
||||
bind_and_activate: bool = ...) -> None: ...
|
||||
|
||||
class ForkingMixIn: ...
|
||||
class ThreadingMixIn: ...
|
||||
|
||||
class ForkingTCPServer(ForkingMixIn, TCPServer): ...
|
||||
class ForkingUDPServer(ForkingMixIn, UDPServer): ...
|
||||
class ThreadingTCPServer(ThreadingMixIn, TCPServer): ...
|
||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer): ...
|
||||
if sys.platform != 'win32':
|
||||
class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): ...
|
||||
class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): ...
|
||||
|
||||
|
||||
class BaseRequestHandler:
|
||||
# Those are technically of types, respectively:
|
||||
# * Union[SocketType, Tuple[bytes, SocketType]]
|
||||
# * Union[Tuple[str, int], str]
|
||||
# But there are some concerns that having unions here would cause
|
||||
# too much inconvenience to people using it (see
|
||||
# https://github.com/python/typeshed/pull/384#issuecomment-234649696)
|
||||
request: Any
|
||||
client_address: Any
|
||||
|
||||
server: BaseServer
|
||||
def setup(self) -> None: ...
|
||||
def handle(self) -> None: ...
|
||||
def finish(self) -> None: ...
|
||||
|
||||
class StreamRequestHandler(BaseRequestHandler):
|
||||
rfile: BinaryIO
|
||||
wfile: BinaryIO
|
||||
|
||||
class DatagramRequestHandler(BaseRequestHandler):
|
||||
rfile: BinaryIO
|
||||
wfile: BinaryIO
|
||||
@@ -0,0 +1,30 @@
|
||||
# Stubs for StringIO (Python 2)
|
||||
|
||||
from typing import Any, IO, AnyStr, Iterator, Iterable, Generic, List, Optional
|
||||
|
||||
class StringIO(IO[AnyStr], Generic[AnyStr]):
|
||||
closed: bool
|
||||
softspace: int
|
||||
len: int
|
||||
name: str
|
||||
def __init__(self, buf: AnyStr = ...) -> None: ...
|
||||
def __iter__(self) -> Iterator[AnyStr]: ...
|
||||
def next(self) -> AnyStr: ...
|
||||
def close(self) -> None: ...
|
||||
def isatty(self) -> bool: ...
|
||||
def seek(self, pos: int, mode: int = ...) -> int: ...
|
||||
def tell(self) -> int: ...
|
||||
def read(self, n: int = ...) -> AnyStr: ...
|
||||
def readline(self, length: int = ...) -> AnyStr: ...
|
||||
def readlines(self, sizehint: int = ...) -> List[AnyStr]: ...
|
||||
def truncate(self, size: Optional[int] = ...) -> int: ...
|
||||
def write(self, s: AnyStr) -> int: ...
|
||||
def writelines(self, iterable: Iterable[AnyStr]) -> None: ...
|
||||
def flush(self) -> None: ...
|
||||
def getvalue(self) -> AnyStr: ...
|
||||
def __enter__(self) -> Any: ...
|
||||
def __exit__(self, type: Any, value: Any, traceback: Any) -> Any: ...
|
||||
def fileno(self) -> int: ...
|
||||
def readable(self) -> bool: ...
|
||||
def seekable(self) -> bool: ...
|
||||
def writable(self) -> bool: ...
|
||||
@@ -0,0 +1,44 @@
|
||||
from typing import (Any, Container, Dict, Generic, Iterable, Iterator, List,
|
||||
Mapping, Optional, Sized, Tuple, TypeVar, Union, overload)
|
||||
|
||||
_KT = TypeVar('_KT')
|
||||
_VT = TypeVar('_VT')
|
||||
_T = TypeVar('_T')
|
||||
|
||||
class UserDict(Dict[_KT, _VT], Generic[_KT, _VT]):
|
||||
data: Dict[_KT, _VT]
|
||||
|
||||
def __init__(self, initialdata: Mapping[_KT, _VT] = ...) -> None: ...
|
||||
|
||||
# TODO: __iter__ is not available for UserDict
|
||||
|
||||
class IterableUserDict(UserDict[_KT, _VT], Generic[_KT, _VT]):
|
||||
...
|
||||
|
||||
class DictMixin(Iterable[_KT], Container[_KT], Sized, Generic[_KT, _VT]):
|
||||
def has_key(self, key: _KT) -> bool: ...
|
||||
def __len__(self) -> int: ...
|
||||
def __iter__(self) -> Iterator[_KT]: ...
|
||||
|
||||
# From typing.Mapping[_KT, _VT]
|
||||
# (can't inherit because of keys())
|
||||
@overload
|
||||
def get(self, k: _KT) -> Optional[_VT]: ...
|
||||
@overload
|
||||
def get(self, k: _KT, default: Union[_VT, _T]) -> Union[_VT, _T]: ...
|
||||
def values(self) -> List[_VT]: ...
|
||||
def items(self) -> List[Tuple[_KT, _VT]]: ...
|
||||
def iterkeys(self) -> Iterator[_KT]: ...
|
||||
def itervalues(self) -> Iterator[_VT]: ...
|
||||
def iteritems(self) -> Iterator[Tuple[_KT, _VT]]: ...
|
||||
def __contains__(self, o: Any) -> bool: ...
|
||||
|
||||
# From typing.MutableMapping[_KT, _VT]
|
||||
def clear(self) -> None: ...
|
||||
def pop(self, k: _KT, default: _VT = ...) -> _VT: ...
|
||||
def popitem(self) -> Tuple[_KT, _VT]: ...
|
||||
def setdefault(self, k: _KT, default: _VT = ...) -> _VT: ...
|
||||
@overload
|
||||
def update(self, m: Mapping[_KT, _VT], **kwargs: _VT) -> None: ...
|
||||
@overload
|
||||
def update(self, m: Iterable[Tuple[_KT, _VT]], **kwargs: _VT) -> None: ...
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import Iterable, MutableSequence, TypeVar, Union, overload, List
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_S = TypeVar("_S")
|
||||
|
||||
class UserList(MutableSequence[_T]):
|
||||
data: List[_T]
|
||||
def insert(self, index: int, object: _T) -> None: ...
|
||||
@overload
|
||||
def __setitem__(self, i: int, o: _T) -> None: ...
|
||||
@overload
|
||||
def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ...
|
||||
def __delitem__(self, i: Union[int, slice]) -> None: ...
|
||||
def __len__(self) -> int: ...
|
||||
@overload
|
||||
def __getitem__(self, i: int) -> _T: ...
|
||||
@overload
|
||||
def __getitem__(self: _S, s: slice) -> _S: ...
|
||||
def sort(self) -> None: ...
|
||||
@@ -0,0 +1,75 @@
|
||||
import collections
|
||||
from typing import Any, Iterable, List, MutableSequence, Sequence, Optional, overload, Text, TypeVar, Tuple, Union
|
||||
|
||||
_UST = TypeVar("_UST", bound=UserString)
|
||||
_MST = TypeVar("_MST", bound=MutableString)
|
||||
|
||||
class UserString(Sequence[UserString]):
|
||||
data: unicode
|
||||
def __init__(self, seq: object) -> None: ...
|
||||
def __int__(self) -> int: ...
|
||||
def __long__(self) -> long: ...
|
||||
def __float__(self) -> float: ...
|
||||
def __complex__(self) -> complex: ...
|
||||
def __hash__(self) -> int: ...
|
||||
def __len__(self) -> int: ...
|
||||
@overload
|
||||
def __getitem__(self: _UST, i: int) -> _UST: ...
|
||||
@overload
|
||||
def __getitem__(self: _UST, s: slice) -> _UST: ...
|
||||
def __add__(self: _UST, other: Any) -> _UST: ...
|
||||
def __radd__(self: _UST, other: Any) -> _UST: ...
|
||||
def __mul__(self: _UST, other: int) -> _UST: ...
|
||||
def __rmul__(self: _UST, other: int) -> _UST: ...
|
||||
def __mod__(self: _UST, args: Any) -> _UST: ...
|
||||
def capitalize(self: _UST) -> _UST: ...
|
||||
def center(self: _UST, width: int, *args: Any) -> _UST: ...
|
||||
def count(self, sub: int, start: int = ..., end: int = ...) -> int: ...
|
||||
def decode(self: _UST, encoding: Optional[str] = ..., errors: Optional[str] = ...) -> _UST: ...
|
||||
def encode(self: _UST, encoding: Optional[str] = ..., errors: Optional[str] = ...) -> _UST: ...
|
||||
def endswith(self, suffix: Text, start: int = ..., end: int = ...) -> bool: ...
|
||||
def expandtabs(self: _UST, tabsize: int = ...) -> _UST: ...
|
||||
def find(self, sub: Text, start: int = ..., end: int = ...) -> int: ...
|
||||
def index(self, sub: Text, start: int = ..., end: int = ...) -> int: ...
|
||||
def isalpha(self) -> bool: ...
|
||||
def isalnum(self) -> bool: ...
|
||||
def isdecimal(self) -> bool: ...
|
||||
def isdigit(self) -> bool: ...
|
||||
def islower(self) -> bool: ...
|
||||
def isnumeric(self) -> bool: ...
|
||||
def isspace(self) -> bool: ...
|
||||
def istitle(self) -> bool: ...
|
||||
def isupper(self) -> bool: ...
|
||||
def join(self, seq: Iterable[Text]) -> Text: ...
|
||||
def ljust(self: _UST, width: int, *args: Any) -> _UST: ...
|
||||
def lower(self: _UST) -> _UST: ...
|
||||
def lstrip(self: _UST, chars: Optional[Text] = ...) -> _UST: ...
|
||||
def partition(self, sep: Text) -> Tuple[Text, Text, Text]: ...
|
||||
def replace(self: _UST, old: Text, new: Text, maxsplit: int = ...) -> _UST: ...
|
||||
def rfind(self, sub: Text, start: int = ..., end: int = ...) -> int: ...
|
||||
def rindex(self, sub: Text, start: int = ..., end: int = ...) -> int: ...
|
||||
def rjust(self: _UST, width: int, *args: Any) -> _UST: ...
|
||||
def rpartition(self, sep: Text) -> Tuple[Text, Text, Text]: ...
|
||||
def rstrip(self: _UST, chars: Optional[Text] = ...) -> _UST: ...
|
||||
def split(self, sep: Optional[Text] = ..., maxsplit: int = ...) -> List[Text]: ...
|
||||
def rsplit(self, sep: Optional[Text] = ..., maxsplit: int = ...) -> List[Text]: ...
|
||||
def splitlines(self, keepends: int = ...) -> List[Text]: ...
|
||||
def startswith(self, suffix: Text, start: int = ..., end: int = ...) -> bool: ...
|
||||
def strip(self: _UST, chars: Optional[Text] = ...) -> _UST: ...
|
||||
def swapcase(self: _UST) -> _UST: ...
|
||||
def title(self: _UST) -> _UST: ...
|
||||
def translate(self: _UST, *args: Any) -> _UST: ...
|
||||
def upper(self: _UST) -> _UST: ...
|
||||
def zfill(self: _UST, width: int) -> _UST: ...
|
||||
|
||||
class MutableString(UserString, MutableSequence[MutableString]):
|
||||
@overload
|
||||
def __getitem__(self: _MST, i: int) -> _MST: ...
|
||||
@overload
|
||||
def __getitem__(self: _MST, s: slice) -> _MST: ...
|
||||
def __setitem__(self, index: Union[int, slice], sub: Any) -> None: ...
|
||||
def __delitem__(self, index: Union[int, slice]) -> None: ...
|
||||
def immutable(self) -> UserString: ...
|
||||
def __iadd__(self: _MST, other: Any) -> _MST: ...
|
||||
def __imul__(self, n: int) -> _MST: ...
|
||||
def insert(self, index: int, value: Any) -> None: ...
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,330 @@
|
||||
import typing
|
||||
from typing import Optional
|
||||
|
||||
__version__: str
|
||||
PyCF_ONLY_AST: int
|
||||
_identifier = str
|
||||
|
||||
class AST:
|
||||
_attributes: typing.Tuple[str, ...]
|
||||
_fields: typing.Tuple[str, ...]
|
||||
def __init__(self, *args, **kwargs) -> None: ...
|
||||
|
||||
class mod(AST):
|
||||
...
|
||||
|
||||
class Module(mod):
|
||||
body: typing.List[stmt]
|
||||
|
||||
class Interactive(mod):
|
||||
body: typing.List[stmt]
|
||||
|
||||
class Expression(mod):
|
||||
body: expr
|
||||
|
||||
class Suite(mod):
|
||||
body: typing.List[stmt]
|
||||
|
||||
|
||||
class stmt(AST):
|
||||
lineno: int
|
||||
col_offset: int
|
||||
|
||||
class FunctionDef(stmt):
|
||||
name: _identifier
|
||||
args: arguments
|
||||
body: typing.List[stmt]
|
||||
decorator_list: typing.List[expr]
|
||||
|
||||
class ClassDef(stmt):
|
||||
name: _identifier
|
||||
bases: typing.List[expr]
|
||||
body: typing.List[stmt]
|
||||
decorator_list: typing.List[expr]
|
||||
|
||||
class Return(stmt):
|
||||
value: Optional[expr]
|
||||
|
||||
class Delete(stmt):
|
||||
targets: typing.List[expr]
|
||||
|
||||
class Assign(stmt):
|
||||
targets: typing.List[expr]
|
||||
value: expr
|
||||
|
||||
class AugAssign(stmt):
|
||||
target: expr
|
||||
op: operator
|
||||
value: expr
|
||||
|
||||
class Print(stmt):
|
||||
dest: Optional[expr]
|
||||
values: typing.List[expr]
|
||||
nl: bool
|
||||
|
||||
class For(stmt):
|
||||
target: expr
|
||||
iter: expr
|
||||
body: typing.List[stmt]
|
||||
orelse: typing.List[stmt]
|
||||
|
||||
class While(stmt):
|
||||
test: expr
|
||||
body: typing.List[stmt]
|
||||
orelse: typing.List[stmt]
|
||||
|
||||
class If(stmt):
|
||||
test: expr
|
||||
body: typing.List[stmt]
|
||||
orelse: typing.List[stmt]
|
||||
|
||||
class With(stmt):
|
||||
context_expr: expr
|
||||
optional_vars: Optional[expr]
|
||||
body: typing.List[stmt]
|
||||
|
||||
class Raise(stmt):
|
||||
type: Optional[expr]
|
||||
inst: Optional[expr]
|
||||
tback: Optional[expr]
|
||||
|
||||
class TryExcept(stmt):
|
||||
body: typing.List[stmt]
|
||||
handlers: typing.List[ExceptHandler]
|
||||
orelse: typing.List[stmt]
|
||||
|
||||
class TryFinally(stmt):
|
||||
body: typing.List[stmt]
|
||||
finalbody: typing.List[stmt]
|
||||
|
||||
class Assert(stmt):
|
||||
test: expr
|
||||
msg: Optional[expr]
|
||||
|
||||
class Import(stmt):
|
||||
names: typing.List[alias]
|
||||
|
||||
class ImportFrom(stmt):
|
||||
module: Optional[_identifier]
|
||||
names: typing.List[alias]
|
||||
level: Optional[int]
|
||||
|
||||
class Exec(stmt):
|
||||
body: expr
|
||||
globals: Optional[expr]
|
||||
locals: Optional[expr]
|
||||
|
||||
class Global(stmt):
|
||||
names: typing.List[_identifier]
|
||||
|
||||
class Expr(stmt):
|
||||
value: expr
|
||||
|
||||
class Pass(stmt): ...
|
||||
class Break(stmt): ...
|
||||
class Continue(stmt): ...
|
||||
|
||||
|
||||
class slice(AST):
|
||||
...
|
||||
|
||||
_slice = slice # this lets us type the variable named 'slice' below
|
||||
|
||||
class Slice(slice):
|
||||
lower: Optional[expr]
|
||||
upper: Optional[expr]
|
||||
step: Optional[expr]
|
||||
|
||||
class ExtSlice(slice):
|
||||
dims: typing.List[slice]
|
||||
|
||||
class Index(slice):
|
||||
value: expr
|
||||
|
||||
class Ellipsis(slice): ...
|
||||
|
||||
|
||||
class expr(AST):
|
||||
lineno: int
|
||||
col_offset: int
|
||||
|
||||
class BoolOp(expr):
|
||||
op: boolop
|
||||
values: typing.List[expr]
|
||||
|
||||
class BinOp(expr):
|
||||
left: expr
|
||||
op: operator
|
||||
right: expr
|
||||
|
||||
class UnaryOp(expr):
|
||||
op: unaryop
|
||||
operand: expr
|
||||
|
||||
class Lambda(expr):
|
||||
args: arguments
|
||||
body: expr
|
||||
|
||||
class IfExp(expr):
|
||||
test: expr
|
||||
body: expr
|
||||
orelse: expr
|
||||
|
||||
class Dict(expr):
|
||||
keys: typing.List[expr]
|
||||
values: typing.List[expr]
|
||||
|
||||
class Set(expr):
|
||||
elts: typing.List[expr]
|
||||
|
||||
class ListComp(expr):
|
||||
elt: expr
|
||||
generators: typing.List[comprehension]
|
||||
|
||||
class SetComp(expr):
|
||||
elt: expr
|
||||
generators: typing.List[comprehension]
|
||||
|
||||
class DictComp(expr):
|
||||
key: expr
|
||||
value: expr
|
||||
generators: typing.List[comprehension]
|
||||
|
||||
class GeneratorExp(expr):
|
||||
elt: expr
|
||||
generators: typing.List[comprehension]
|
||||
|
||||
class Yield(expr):
|
||||
value: Optional[expr]
|
||||
|
||||
class Compare(expr):
|
||||
left: expr
|
||||
ops: typing.List[cmpop]
|
||||
comparators: typing.List[expr]
|
||||
|
||||
class Call(expr):
|
||||
func: expr
|
||||
args: typing.List[expr]
|
||||
keywords: typing.List[keyword]
|
||||
starargs: Optional[expr]
|
||||
kwargs: Optional[expr]
|
||||
|
||||
class Repr(expr):
|
||||
value: expr
|
||||
|
||||
class Num(expr):
|
||||
n: float
|
||||
|
||||
class Str(expr):
|
||||
s: str
|
||||
|
||||
class Attribute(expr):
|
||||
value: expr
|
||||
attr: _identifier
|
||||
ctx: expr_context
|
||||
|
||||
class Subscript(expr):
|
||||
value: expr
|
||||
slice: _slice
|
||||
ctx: expr_context
|
||||
|
||||
class Name(expr):
|
||||
id: _identifier
|
||||
ctx: expr_context
|
||||
|
||||
class List(expr):
|
||||
elts: typing.List[expr]
|
||||
ctx: expr_context
|
||||
|
||||
class Tuple(expr):
|
||||
elts: typing.List[expr]
|
||||
ctx: expr_context
|
||||
|
||||
|
||||
class expr_context(AST):
|
||||
...
|
||||
|
||||
class AugLoad(expr_context): ...
|
||||
class AugStore(expr_context): ...
|
||||
class Del(expr_context): ...
|
||||
class Load(expr_context): ...
|
||||
class Param(expr_context): ...
|
||||
class Store(expr_context): ...
|
||||
|
||||
|
||||
class boolop(AST):
|
||||
...
|
||||
|
||||
class And(boolop): ...
|
||||
class Or(boolop): ...
|
||||
|
||||
class operator(AST):
|
||||
...
|
||||
|
||||
class Add(operator): ...
|
||||
class BitAnd(operator): ...
|
||||
class BitOr(operator): ...
|
||||
class BitXor(operator): ...
|
||||
class Div(operator): ...
|
||||
class FloorDiv(operator): ...
|
||||
class LShift(operator): ...
|
||||
class Mod(operator): ...
|
||||
class Mult(operator): ...
|
||||
class Pow(operator): ...
|
||||
class RShift(operator): ...
|
||||
class Sub(operator): ...
|
||||
|
||||
class unaryop(AST):
|
||||
...
|
||||
|
||||
class Invert(unaryop): ...
|
||||
class Not(unaryop): ...
|
||||
class UAdd(unaryop): ...
|
||||
class USub(unaryop): ...
|
||||
|
||||
class cmpop(AST):
|
||||
...
|
||||
|
||||
class Eq(cmpop): ...
|
||||
class Gt(cmpop): ...
|
||||
class GtE(cmpop): ...
|
||||
class In(cmpop): ...
|
||||
class Is(cmpop): ...
|
||||
class IsNot(cmpop): ...
|
||||
class Lt(cmpop): ...
|
||||
class LtE(cmpop): ...
|
||||
class NotEq(cmpop): ...
|
||||
class NotIn(cmpop): ...
|
||||
|
||||
|
||||
class comprehension(AST):
|
||||
target: expr
|
||||
iter: expr
|
||||
ifs: typing.List[expr]
|
||||
|
||||
|
||||
class excepthandler(AST):
|
||||
...
|
||||
|
||||
|
||||
class ExceptHandler(excepthandler):
|
||||
type: Optional[expr]
|
||||
name: Optional[expr]
|
||||
body: typing.List[stmt]
|
||||
lineno: int
|
||||
col_offset: int
|
||||
|
||||
|
||||
class arguments(AST):
|
||||
args: typing.List[expr]
|
||||
vararg: Optional[_identifier]
|
||||
kwarg: Optional[_identifier]
|
||||
defaults: typing.List[expr]
|
||||
|
||||
class keyword(AST):
|
||||
arg: _identifier
|
||||
value: expr
|
||||
|
||||
class alias(AST):
|
||||
name: _identifier
|
||||
asname: Optional[_identifier]
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Stub file for the '_collections' module."""
|
||||
|
||||
from typing import Any, Callable, Dict, Generic, Iterator, TypeVar, Optional, Union
|
||||
|
||||
_K = TypeVar("_K")
|
||||
_V = TypeVar("_V")
|
||||
_T = TypeVar('_T')
|
||||
_T2 = TypeVar('_T2')
|
||||
|
||||
class defaultdict(Dict[_K, _V]):
|
||||
default_factory: None
|
||||
def __init__(self, __default_factory: Callable[[], _V] = ..., init: Any = ...) -> None: ...
|
||||
def __missing__(self, key: _K) -> _V: ...
|
||||
def __copy__(self: _T) -> _T: ...
|
||||
def copy(self: _T) -> _T: ...
|
||||
|
||||
class deque(Generic[_T]):
|
||||
maxlen: Optional[int]
|
||||
def __init__(self, iterable: Iterator[_T] = ..., maxlen: int = ...) -> None: ...
|
||||
def append(self, x: _T) -> None: ...
|
||||
def appendleft(self, x: _T) -> None: ...
|
||||
def clear(self) -> None: ...
|
||||
def count(self, x: Any) -> int: ...
|
||||
def extend(self, iterable: Iterator[_T]) -> None: ...
|
||||
def extendleft(self, iterable: Iterator[_T]) -> None: ...
|
||||
def pop(self) -> _T: ...
|
||||
def popleft(self) -> _T: ...
|
||||
def remove(self, value: _T) -> None: ...
|
||||
def reverse(self) -> None: ...
|
||||
def rotate(self, n: int = ...) -> None: ...
|
||||
def __contains__(self, o: Any) -> bool: ...
|
||||
def __copy__(self) -> deque[_T]: ...
|
||||
def __getitem__(self, i: int) -> _T: ...
|
||||
def __iadd__(self, other: deque[_T2]) -> deque[Union[_T, _T2]]: ...
|
||||
def __iter__(self) -> Iterator[_T]: ...
|
||||
def __len__(self) -> int: ...
|
||||
def __reversed__(self) -> Iterator[_T]: ...
|
||||
def __setitem__(self, i: int, x: _T) -> None: ...
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Stub file for the '_functools' module."""
|
||||
|
||||
from typing import Any, Callable, Dict, Iterable, Optional, TypeVar, Tuple, overload
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_S = TypeVar("_S")
|
||||
|
||||
@overload
|
||||
def reduce(function: Callable[[_T, _T], _T],
|
||||
sequence: Iterable[_T]) -> _T: ...
|
||||
@overload
|
||||
def reduce(function: Callable[[_T, _S], _T],
|
||||
sequence: Iterable[_S], initial: _T) -> _T: ...
|
||||
|
||||
class partial(object):
|
||||
func: Callable[..., Any]
|
||||
args: Tuple[Any, ...]
|
||||
keywords: Dict[str, Any]
|
||||
def __init__(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> None: ...
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Stub file for the '_hotshot' module."""
|
||||
# This is an autogenerated file. It serves as a starting point
|
||||
# for a more precise manual annotation of this module.
|
||||
# Feel free to edit the source below, but remove this header when you do.
|
||||
|
||||
from typing import Any, List, Tuple, Dict, Generic
|
||||
|
||||
def coverage(a: str) -> Any: ...
|
||||
def logreader(a: str) -> LogReaderType: ...
|
||||
def profiler(a: str, *args, **kwargs) -> Any: ...
|
||||
def resolution() -> Tuple[Any, ...]: ...
|
||||
|
||||
class LogReaderType(object):
|
||||
def close(self) -> None: ...
|
||||
def fileno(self) -> int: ...
|
||||
|
||||
class ProfilerType(object):
|
||||
def addinfo(self, a: str, b: str) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def fileno(self) -> int: ...
|
||||
def runcall(self, *args, **kwargs) -> Any: ...
|
||||
def runcode(self, a, b, *args, **kwargs) -> Any: ...
|
||||
def start(self) -> None: ...
|
||||
def stop(self) -> None: ...
|
||||
@@ -0,0 +1,186 @@
|
||||
from typing import Any, AnyStr, BinaryIO, IO, Text, TextIO, Iterable, Iterator, List, Optional, Type, Tuple, TypeVar, Union
|
||||
from mmap import mmap
|
||||
from types import TracebackType
|
||||
|
||||
_bytearray_like = Union[bytearray, mmap]
|
||||
|
||||
DEFAULT_BUFFER_SIZE: int
|
||||
|
||||
class BlockingIOError(IOError):
|
||||
characters_written: int
|
||||
|
||||
class UnsupportedOperation(ValueError, IOError): ...
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class _IOBase(BinaryIO):
|
||||
@property
|
||||
def closed(self) -> bool: ...
|
||||
def _checkClosed(self, msg: Optional[str] = ...) -> None: ... # undocumented
|
||||
def _checkReadable(self) -> None: ...
|
||||
def _checkSeekable(self) -> None: ...
|
||||
def _checkWritable(self) -> None: ...
|
||||
# All these methods are concrete here (you can instantiate this)
|
||||
def close(self) -> None: ...
|
||||
def fileno(self) -> int: ...
|
||||
def flush(self) -> None: ...
|
||||
def isatty(self) -> bool: ...
|
||||
def readable(self) -> bool: ...
|
||||
def seek(self, offset: int, whence: int = ...) -> int: ...
|
||||
def seekable(self) -> bool: ...
|
||||
def tell(self) -> int: ...
|
||||
def truncate(self, size: Optional[int] = ...) -> int: ...
|
||||
def writable(self) -> bool: ...
|
||||
def __enter__(self: _T) -> _T: ...
|
||||
def __exit__(self, t: Optional[Type[BaseException]], value: Optional[BaseException], traceback: Optional[Any]) -> Optional[bool]: ...
|
||||
def __iter__(self: _T) -> _T: ...
|
||||
# The parameter type of writelines[s]() is determined by that of write():
|
||||
def writelines(self, lines: Iterable[bytes]) -> None: ...
|
||||
# The return type of readline[s]() and next() is determined by that of read():
|
||||
def readline(self, limit: int = ...) -> bytes: ...
|
||||
def readlines(self, hint: int = ...) -> List[bytes]: ...
|
||||
def next(self) -> bytes: ...
|
||||
# These don't actually exist but we need to pretend that it does
|
||||
# so that this class is concrete.
|
||||
def write(self, s: bytes) -> int: ...
|
||||
def read(self, n: int = ...) -> bytes: ...
|
||||
|
||||
class _BufferedIOBase(_IOBase):
|
||||
def read1(self, n: int) -> bytes: ...
|
||||
def read(self, size: int = ...) -> bytes: ...
|
||||
def readinto(self, buffer: _bytearray_like) -> int: ...
|
||||
def write(self, s: bytes) -> int: ...
|
||||
def detach(self) -> _IOBase: ...
|
||||
|
||||
class BufferedRWPair(_BufferedIOBase):
|
||||
def __init__(self, reader: _RawIOBase, writer: _RawIOBase,
|
||||
buffer_size: int = ..., max_buffer_size: int = ...) -> None: ...
|
||||
def peek(self, n: int = ...) -> bytes: ...
|
||||
def __enter__(self) -> BufferedRWPair: ...
|
||||
|
||||
class BufferedRandom(_BufferedIOBase):
|
||||
mode: str
|
||||
name: str
|
||||
raw: _IOBase
|
||||
def __init__(self, raw: _IOBase,
|
||||
buffer_size: int = ...,
|
||||
max_buffer_size: int = ...) -> None: ...
|
||||
def peek(self, n: int = ...) -> bytes: ...
|
||||
|
||||
class BufferedReader(_BufferedIOBase):
|
||||
mode: str
|
||||
name: str
|
||||
raw: _IOBase
|
||||
def __init__(self, raw: _IOBase, buffer_size: int = ...) -> None: ...
|
||||
def peek(self, n: int = ...) -> bytes: ...
|
||||
|
||||
class BufferedWriter(_BufferedIOBase):
|
||||
name: str
|
||||
raw: _IOBase
|
||||
mode: str
|
||||
def __init__(self, raw: _IOBase,
|
||||
buffer_size: int = ...,
|
||||
max_buffer_size: int = ...) -> None: ...
|
||||
|
||||
class BytesIO(_BufferedIOBase):
|
||||
def __init__(self, initial_bytes: bytes = ...) -> None: ...
|
||||
def __setstate__(self, state: Tuple[Any, ...]) -> None: ...
|
||||
def __getstate__(self) -> Tuple[Any, ...]: ...
|
||||
# BytesIO does not contain a "name" field. This workaround is necessary
|
||||
# to allow BytesIO sub-classes to add this field, as it is defined
|
||||
# as a read-only property on IO[].
|
||||
name: Any
|
||||
def getvalue(self) -> bytes: ...
|
||||
def write(self, s: bytes) -> int: ...
|
||||
def writelines(self, lines: Iterable[bytes]) -> None: ...
|
||||
def read1(self, size: int) -> bytes: ...
|
||||
def next(self) -> bytes: ...
|
||||
|
||||
class _RawIOBase(_IOBase):
|
||||
def readall(self) -> str: ...
|
||||
def read(self, n: int = ...) -> str: ...
|
||||
|
||||
class FileIO(_RawIOBase, BytesIO):
|
||||
mode: str
|
||||
closefd: bool
|
||||
def __init__(self, file: Union[str, int], mode: str = ..., closefd: bool = ...) -> None: ...
|
||||
def readinto(self, buffer: _bytearray_like) -> int: ...
|
||||
def write(self, pbuf: str) -> int: ...
|
||||
|
||||
class IncrementalNewlineDecoder(object):
|
||||
newlines: Union[str, unicode]
|
||||
def __init__(self, decoder, translate, z=...) -> None: ...
|
||||
def decode(self, input, final) -> Any: ...
|
||||
def getstate(self) -> Tuple[Any, int]: ...
|
||||
def setstate(self, state: Tuple[Any, int]) -> None: ...
|
||||
def reset(self) -> None: ...
|
||||
|
||||
|
||||
# Note: In the actual _io.py, _TextIOBase inherits from _IOBase.
|
||||
class _TextIOBase(TextIO):
|
||||
errors: Optional[str]
|
||||
# TODO: On _TextIOBase, this is always None. But it's unicode/bytes in subclasses.
|
||||
newlines: Union[None, unicode, bytes]
|
||||
encoding: str
|
||||
@property
|
||||
def closed(self) -> bool: ...
|
||||
def _checkClosed(self) -> None: ...
|
||||
def _checkReadable(self) -> None: ...
|
||||
def _checkSeekable(self) -> None: ...
|
||||
def _checkWritable(self) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def detach(self) -> IO[Any]: ...
|
||||
def fileno(self) -> int: ...
|
||||
def flush(self) -> None: ...
|
||||
def isatty(self) -> bool: ...
|
||||
def next(self) -> unicode: ...
|
||||
def read(self, size: int = ...) -> unicode: ...
|
||||
def readable(self) -> bool: ...
|
||||
def readline(self, limit: int = ...) -> unicode: ...
|
||||
def readlines(self, hint: int = ...) -> list[unicode]: ...
|
||||
def seek(self, offset: int, whence: int = ...) -> int: ...
|
||||
def seekable(self) -> bool: ...
|
||||
def tell(self) -> int: ...
|
||||
def truncate(self, size: Optional[int] = ...) -> int: ...
|
||||
def writable(self) -> bool: ...
|
||||
def write(self, pbuf: unicode) -> int: ...
|
||||
def writelines(self, lines: Iterable[unicode]) -> None: ...
|
||||
def __enter__(self: _T) -> _T: ...
|
||||
def __exit__(self, t: Optional[Type[BaseException]], value: Optional[BaseException], traceback: Optional[Any]) -> Optional[bool]: ...
|
||||
def __iter__(self: _T) -> _T: ...
|
||||
|
||||
class StringIO(_TextIOBase):
|
||||
line_buffering: bool
|
||||
def __init__(self,
|
||||
initial_value: Optional[unicode] = ...,
|
||||
newline: Optional[unicode] = ...) -> None: ...
|
||||
def __setstate__(self, state: Tuple[Any, ...]) -> None: ...
|
||||
def __getstate__(self) -> Tuple[Any, ...]: ...
|
||||
# StringIO does not contain a "name" field. This workaround is necessary
|
||||
# to allow StringIO sub-classes to add this field, as it is defined
|
||||
# as a read-only property on IO[].
|
||||
name: Any
|
||||
def getvalue(self) -> unicode: ...
|
||||
|
||||
class TextIOWrapper(_TextIOBase):
|
||||
name: str
|
||||
line_buffering: bool
|
||||
buffer: BinaryIO
|
||||
_CHUNK_SIZE: int
|
||||
def __init__(
|
||||
self,
|
||||
buffer: IO[Any],
|
||||
encoding: Optional[Text] = ...,
|
||||
errors: Optional[Text] = ...,
|
||||
newline: Optional[Text] = ...,
|
||||
line_buffering: bool = ...,
|
||||
write_through: bool = ...,
|
||||
) -> None: ...
|
||||
|
||||
def open(file: Union[str, unicode, int],
|
||||
mode: Text = ...,
|
||||
buffering: int = ...,
|
||||
encoding: Optional[Text] = ...,
|
||||
errors: Optional[Text] = ...,
|
||||
newline: Optional[Text] = ...,
|
||||
closefd: bool = ...) -> IO[Any]: ...
|
||||
@@ -0,0 +1,7 @@
|
||||
from typing import Any, List, Tuple, Dict, Generic, Tuple
|
||||
|
||||
def encode_basestring_ascii(*args, **kwargs) -> str: ...
|
||||
def scanstring(a, b, *args, **kwargs) -> Tuple[Any, ...]: ...
|
||||
|
||||
class Encoder(object): ...
|
||||
class Scanner(object): ...
|
||||
@@ -0,0 +1,13 @@
|
||||
blocksize: int
|
||||
digest_size: int
|
||||
|
||||
class MD5Type(object):
|
||||
name: str
|
||||
block_size: int
|
||||
digest_size: int
|
||||
def copy(self) -> MD5Type: ...
|
||||
def digest(self) -> str: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, arg: str) -> None: ...
|
||||
|
||||
def new(arg: str = ...) -> MD5Type: ...
|
||||
@@ -0,0 +1,15 @@
|
||||
blocksize: int
|
||||
block_size: int
|
||||
digest_size: int
|
||||
|
||||
class sha(object): # not actually exposed
|
||||
name: str
|
||||
block_size: int
|
||||
digest_size: int
|
||||
digestsize: int
|
||||
def copy(self) -> sha: ...
|
||||
def digest(self) -> str: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, arg: str) -> None: ...
|
||||
|
||||
def new(arg: str = ...) -> sha: ...
|
||||
@@ -0,0 +1,23 @@
|
||||
from typing import Optional
|
||||
|
||||
class sha224(object):
|
||||
name: str
|
||||
block_size: int
|
||||
digest_size: int
|
||||
digestsize: int
|
||||
def __init__(self, init: Optional[str]) -> None: ...
|
||||
def copy(self) -> sha224: ...
|
||||
def digest(self) -> str: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, arg: str) -> None: ...
|
||||
|
||||
class sha256(object):
|
||||
name: str
|
||||
block_size: int
|
||||
digest_size: int
|
||||
digestsize: int
|
||||
def __init__(self, init: Optional[str]) -> None: ...
|
||||
def copy(self) -> sha256: ...
|
||||
def digest(self) -> str: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, arg: str) -> None: ...
|
||||
@@ -0,0 +1,23 @@
|
||||
from typing import Optional
|
||||
|
||||
class sha384(object):
|
||||
name: str
|
||||
block_size: int
|
||||
digest_size: int
|
||||
digestsize: int
|
||||
def __init__(self, init: Optional[str]) -> None: ...
|
||||
def copy(self) -> sha384: ...
|
||||
def digest(self) -> str: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, arg: str) -> None: ...
|
||||
|
||||
class sha512(object):
|
||||
name: str
|
||||
block_size: int
|
||||
digest_size: int
|
||||
digestsize: int
|
||||
def __init__(self, init: Optional[str]) -> None: ...
|
||||
def copy(self) -> sha512: ...
|
||||
def digest(self) -> str: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, arg: str) -> None: ...
|
||||
@@ -0,0 +1,283 @@
|
||||
from typing import Tuple, Union, IO, Any, Optional, overload
|
||||
|
||||
AF_APPLETALK: int
|
||||
AF_ASH: int
|
||||
AF_ATMPVC: int
|
||||
AF_ATMSVC: int
|
||||
AF_AX25: int
|
||||
AF_BLUETOOTH: int
|
||||
AF_BRIDGE: int
|
||||
AF_DECnet: int
|
||||
AF_ECONET: int
|
||||
AF_INET: int
|
||||
AF_INET6: int
|
||||
AF_IPX: int
|
||||
AF_IRDA: int
|
||||
AF_KEY: int
|
||||
AF_LLC: int
|
||||
AF_NETBEUI: int
|
||||
AF_NETLINK: int
|
||||
AF_NETROM: int
|
||||
AF_PACKET: int
|
||||
AF_PPPOX: int
|
||||
AF_ROSE: int
|
||||
AF_ROUTE: int
|
||||
AF_SECURITY: int
|
||||
AF_SNA: int
|
||||
AF_TIPC: int
|
||||
AF_UNIX: int
|
||||
AF_UNSPEC: int
|
||||
AF_WANPIPE: int
|
||||
AF_X25: int
|
||||
AI_ADDRCONFIG: int
|
||||
AI_ALL: int
|
||||
AI_CANONNAME: int
|
||||
AI_NUMERICHOST: int
|
||||
AI_NUMERICSERV: int
|
||||
AI_PASSIVE: int
|
||||
AI_V4MAPPED: int
|
||||
BDADDR_ANY: str
|
||||
BDADDR_LOCAL: str
|
||||
BTPROTO_HCI: int
|
||||
BTPROTO_L2CAP: int
|
||||
BTPROTO_RFCOMM: int
|
||||
BTPROTO_SCO: int
|
||||
EAI_ADDRFAMILY: int
|
||||
EAI_AGAIN: int
|
||||
EAI_BADFLAGS: int
|
||||
EAI_FAIL: int
|
||||
EAI_FAMILY: int
|
||||
EAI_MEMORY: int
|
||||
EAI_NODATA: int
|
||||
EAI_NONAME: int
|
||||
EAI_OVERFLOW: int
|
||||
EAI_SERVICE: int
|
||||
EAI_SOCKTYPE: int
|
||||
EAI_SYSTEM: int
|
||||
EBADF: int
|
||||
EINTR: int
|
||||
HCI_DATA_DIR: int
|
||||
HCI_FILTER: int
|
||||
HCI_TIME_STAMP: 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
|
||||
IPPORT_RESERVED: int
|
||||
IPPORT_USERRESERVED: int
|
||||
IPPROTO_AH: int
|
||||
IPPROTO_DSTOPTS: int
|
||||
IPPROTO_EGP: int
|
||||
IPPROTO_ESP: int
|
||||
IPPROTO_FRAGMENT: int
|
||||
IPPROTO_GRE: int
|
||||
IPPROTO_HOPOPTS: int
|
||||
IPPROTO_ICMP: int
|
||||
IPPROTO_ICMPV6: int
|
||||
IPPROTO_IDP: int
|
||||
IPPROTO_IGMP: int
|
||||
IPPROTO_IP: int
|
||||
IPPROTO_IPIP: int
|
||||
IPPROTO_IPV6: int
|
||||
IPPROTO_NONE: int
|
||||
IPPROTO_PIM: int
|
||||
IPPROTO_PUP: int
|
||||
IPPROTO_RAW: int
|
||||
IPPROTO_ROUTING: int
|
||||
IPPROTO_RSVP: int
|
||||
IPPROTO_TCP: int
|
||||
IPPROTO_TP: int
|
||||
IPPROTO_UDP: int
|
||||
IPV6_CHECKSUM: int
|
||||
IPV6_DSTOPTS: int
|
||||
IPV6_HOPLIMIT: int
|
||||
IPV6_HOPOPTS: int
|
||||
IPV6_JOIN_GROUP: int
|
||||
IPV6_LEAVE_GROUP: int
|
||||
IPV6_MULTICAST_HOPS: int
|
||||
IPV6_MULTICAST_IF: int
|
||||
IPV6_MULTICAST_LOOP: int
|
||||
IPV6_NEXTHOP: int
|
||||
IPV6_PKTINFO: int
|
||||
IPV6_RECVDSTOPTS: int
|
||||
IPV6_RECVHOPLIMIT: int
|
||||
IPV6_RECVHOPOPTS: int
|
||||
IPV6_RECVPKTINFO: int
|
||||
IPV6_RECVRTHDR: int
|
||||
IPV6_RECVTCLASS: int
|
||||
IPV6_RTHDR: int
|
||||
IPV6_RTHDRDSTOPTS: int
|
||||
IPV6_RTHDR_TYPE_0: int
|
||||
IPV6_TCLASS: int
|
||||
IPV6_UNICAST_HOPS: int
|
||||
IPV6_V6ONLY: int
|
||||
IP_ADD_MEMBERSHIP: int
|
||||
IP_DEFAULT_MULTICAST_LOOP: int
|
||||
IP_DEFAULT_MULTICAST_TTL: int
|
||||
IP_DROP_MEMBERSHIP: int
|
||||
IP_HDRINCL: int
|
||||
IP_MAX_MEMBERSHIPS: int
|
||||
IP_MULTICAST_IF: int
|
||||
IP_MULTICAST_LOOP: int
|
||||
IP_MULTICAST_TTL: int
|
||||
IP_OPTIONS: int
|
||||
IP_RECVOPTS: int
|
||||
IP_RECVRETOPTS: int
|
||||
IP_RETOPTS: int
|
||||
IP_TOS: int
|
||||
IP_TTL: int
|
||||
MSG_CTRUNC: int
|
||||
MSG_DONTROUTE: int
|
||||
MSG_DONTWAIT: int
|
||||
MSG_EOR: int
|
||||
MSG_OOB: int
|
||||
MSG_PEEK: int
|
||||
MSG_TRUNC: int
|
||||
MSG_WAITALL: int
|
||||
MethodType: type
|
||||
NETLINK_DNRTMSG: int
|
||||
NETLINK_FIREWALL: int
|
||||
NETLINK_IP6_FW: int
|
||||
NETLINK_NFLOG: int
|
||||
NETLINK_ROUTE: int
|
||||
NETLINK_USERSOCK: int
|
||||
NETLINK_XFRM: int
|
||||
NI_DGRAM: int
|
||||
NI_MAXHOST: int
|
||||
NI_MAXSERV: int
|
||||
NI_NAMEREQD: int
|
||||
NI_NOFQDN: int
|
||||
NI_NUMERICHOST: int
|
||||
NI_NUMERICSERV: int
|
||||
PACKET_BROADCAST: int
|
||||
PACKET_FASTROUTE: int
|
||||
PACKET_HOST: int
|
||||
PACKET_LOOPBACK: int
|
||||
PACKET_MULTICAST: int
|
||||
PACKET_OTHERHOST: int
|
||||
PACKET_OUTGOING: int
|
||||
PF_PACKET: int
|
||||
SHUT_RD: int
|
||||
SHUT_RDWR: int
|
||||
SHUT_WR: int
|
||||
SOCK_DGRAM: int
|
||||
SOCK_RAW: int
|
||||
SOCK_RDM: int
|
||||
SOCK_SEQPACKET: int
|
||||
SOCK_STREAM: int
|
||||
SOL_HCI: int
|
||||
SOL_IP: int
|
||||
SOL_SOCKET: int
|
||||
SOL_TCP: int
|
||||
SOL_TIPC: int
|
||||
SOL_UDP: int
|
||||
SOMAXCONN: int
|
||||
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_REUSEPORT: int
|
||||
SO_SNDBUF: int
|
||||
SO_SNDLOWAT: int
|
||||
SO_SNDTIMEO: int
|
||||
SO_TYPE: int
|
||||
SSL_ERROR_EOF: int
|
||||
SSL_ERROR_INVALID_ERROR_CODE: int
|
||||
SSL_ERROR_SSL: int
|
||||
SSL_ERROR_SYSCALL: int
|
||||
SSL_ERROR_WANT_CONNECT: int
|
||||
SSL_ERROR_WANT_READ: int
|
||||
SSL_ERROR_WANT_WRITE: int
|
||||
SSL_ERROR_WANT_X509_LOOKUP: int
|
||||
SSL_ERROR_ZERO_RETURN: int
|
||||
TCP_CORK: int
|
||||
TCP_DEFER_ACCEPT: int
|
||||
TCP_INFO: int
|
||||
TCP_KEEPCNT: int
|
||||
TCP_KEEPIDLE: int
|
||||
TCP_KEEPINTVL: int
|
||||
TCP_LINGER2: int
|
||||
TCP_MAXSEG: int
|
||||
TCP_NODELAY: int
|
||||
TCP_QUICKACK: int
|
||||
TCP_SYNCNT: int
|
||||
TCP_WINDOW_CLAMP: 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
|
||||
|
||||
# PyCapsule
|
||||
CAPI: Any
|
||||
|
||||
has_ipv6: bool
|
||||
|
||||
class error(IOError): ...
|
||||
class gaierror(error): ...
|
||||
class timeout(error): ...
|
||||
|
||||
class SocketType(object):
|
||||
family: int
|
||||
type: int
|
||||
proto: int
|
||||
timeout: float
|
||||
|
||||
def __init__(self, family: int = ..., type: int = ..., proto: int = ...) -> None: ...
|
||||
def accept(self) -> Tuple[SocketType, Tuple[Any, ...]]: ...
|
||||
def bind(self, address: Tuple[Any, ...]) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def connect(self, address: Tuple[Any, ...]) -> None: ...
|
||||
def connect_ex(self, address: Tuple[Any, ...]) -> int: ...
|
||||
def dup(self) -> SocketType: ...
|
||||
def fileno(self) -> int: ...
|
||||
def getpeername(self) -> Tuple[Any, ...]: ...
|
||||
def getsockname(self) -> Tuple[Any, ...]: ...
|
||||
def getsockopt(self, level: int, option: int, buffersize: int = ...) -> str: ...
|
||||
def gettimeout(self) -> float: ...
|
||||
def listen(self, backlog: int) -> None: ...
|
||||
def makefile(self, mode: str = ..., buffersize: int = ...) -> IO[Any]: ...
|
||||
def recv(self, buffersize: int, flags: int = ...) -> str: ...
|
||||
def recv_into(self, buffer: bytearray, nbytes: int = ..., flags: int = ...) -> int: ...
|
||||
def recvfrom(self, buffersize: int, flags: int = ...) -> Tuple[Any, ...]: ...
|
||||
def recvfrom_into(self, buffer: bytearray, nbytes: int = ...,
|
||||
flags: int = ...) -> int: ...
|
||||
def send(self, data: str, flags: int = ...) -> int: ...
|
||||
def sendall(self, data: str, flags: int = ...) -> None: ...
|
||||
@overload
|
||||
def sendto(self, data: str, address: Tuple[Any, ...]) -> int: ...
|
||||
@overload
|
||||
def sendto(self, data: str, flags: int, address: Tuple[Any, ...]) -> int: ...
|
||||
def setblocking(self, flag: bool) -> None: ...
|
||||
def setsockopt(self, level: int, option: int, value: Union[int, str]) -> None: ...
|
||||
def settimeout(self, value: Optional[float]) -> None: ...
|
||||
def shutdown(self, flag: int) -> None: ...
|
||||
@@ -0,0 +1,55 @@
|
||||
"""Stub file for the '_sre' module."""
|
||||
|
||||
from typing import Any, Union, Iterable, Optional, Mapping, Sequence, Dict, List, Tuple, overload
|
||||
|
||||
CODESIZE: int
|
||||
MAGIC: int
|
||||
MAXREPEAT: long
|
||||
copyright: str
|
||||
|
||||
class SRE_Match(object):
|
||||
def start(self, group: int = ...) -> int: ...
|
||||
def end(self, group: int = ...) -> int: ...
|
||||
def expand(self, s: str) -> Any: ...
|
||||
@overload
|
||||
def group(self) -> str: ...
|
||||
@overload
|
||||
def group(self, group: int = ...) -> Optional[str]: ...
|
||||
def groupdict(self) -> Dict[int, Optional[str]]: ...
|
||||
def groups(self) -> Tuple[Optional[str], ...]: ...
|
||||
def span(self) -> Tuple[int, int]: ...
|
||||
@property
|
||||
def regs(self) -> Tuple[Tuple[int, int], ...]: ... # undocumented
|
||||
|
||||
class SRE_Scanner(object):
|
||||
pattern: str
|
||||
def match(self) -> SRE_Match: ...
|
||||
def search(self) -> SRE_Match: ...
|
||||
|
||||
class SRE_Pattern(object):
|
||||
pattern: str
|
||||
flags: int
|
||||
groups: int
|
||||
groupindex: Mapping[str, int]
|
||||
indexgroup: Sequence[int]
|
||||
def findall(self, source: str, pos: int = ..., endpos: int = ...) -> List[Union[Tuple[Any, ...], str]]: ...
|
||||
def finditer(self, source: str, pos: int = ..., endpos: int = ...) -> Iterable[Union[Tuple[Any, ...], str]]: ...
|
||||
def match(self, pattern, pos: int = ..., endpos: int = ...) -> SRE_Match: ...
|
||||
def scanner(self, s: str, start: int = ..., end: int = ...) -> SRE_Scanner: ...
|
||||
def search(self, pattern, pos: int = ..., endpos: int = ...) -> SRE_Match: ...
|
||||
def split(self, source: str, maxsplit: int = ...) -> List[Optional[str]]: ...
|
||||
def sub(self, repl: str, string: str, count: int = ...) -> Tuple[Any, ...]: ...
|
||||
def subn(self, repl: str, string: str, count: int = ...) -> Tuple[Any, ...]: ...
|
||||
|
||||
def compile(
|
||||
pattern: str,
|
||||
flags: int,
|
||||
code: List[int],
|
||||
groups: int = ...,
|
||||
groupindex: Mapping[str, int] = ...,
|
||||
indexgroup: Sequence[int] = ...,
|
||||
) -> SRE_Pattern: ...
|
||||
|
||||
def getcodesize() -> int: ...
|
||||
|
||||
def getlower(a: int, b: int) -> int: ...
|
||||
@@ -0,0 +1,22 @@
|
||||
"""Stub file for the '_struct' module."""
|
||||
|
||||
from typing import Any, AnyStr, Tuple
|
||||
|
||||
class error(Exception): ...
|
||||
|
||||
class Struct(object):
|
||||
size: int
|
||||
format: str
|
||||
|
||||
def __init__(self, fmt: str) -> None: ...
|
||||
def pack_into(self, buffer: bytearray, offset: int, obj: Any) -> None: ...
|
||||
def pack(self, *args) -> str: ...
|
||||
def unpack(self, s: str) -> Tuple[Any, ...]: ...
|
||||
def unpack_from(self, buffer: bytearray, offset: int = ...) -> Tuple[Any, ...]: ...
|
||||
|
||||
def _clearcache() -> None: ...
|
||||
def calcsize(fmt: str) -> int: ...
|
||||
def pack(fmt: AnyStr, obj: Any) -> str: ...
|
||||
def pack_into(fmt: AnyStr, buffer: bytearray, offset: int, obj: Any) -> None: ...
|
||||
def unpack(fmt: AnyStr, data: str) -> Tuple[Any, ...]: ...
|
||||
def unpack_from(fmt: AnyStr, buffer: bytearray, offset: int = ...) -> Tuple[Any, ...]: ...
|
||||
@@ -0,0 +1,39 @@
|
||||
from typing import List, Dict
|
||||
|
||||
CELL: int
|
||||
DEF_BOUND: int
|
||||
DEF_FREE: int
|
||||
DEF_FREE_CLASS: int
|
||||
DEF_GLOBAL: int
|
||||
DEF_IMPORT: int
|
||||
DEF_LOCAL: int
|
||||
DEF_PARAM: int
|
||||
FREE: int
|
||||
GLOBAL_EXPLICIT: int
|
||||
GLOBAL_IMPLICIT: int
|
||||
LOCAL: int
|
||||
OPT_BARE_EXEC: int
|
||||
OPT_EXEC: int
|
||||
OPT_IMPORT_STAR: int
|
||||
SCOPE_MASK: int
|
||||
SCOPE_OFF: int
|
||||
TYPE_CLASS: int
|
||||
TYPE_FUNCTION: int
|
||||
TYPE_MODULE: int
|
||||
USE: int
|
||||
|
||||
class _symtable_entry(object):
|
||||
...
|
||||
|
||||
class symtable(object):
|
||||
children: List[_symtable_entry]
|
||||
id: int
|
||||
lineno: int
|
||||
name: str
|
||||
nested: int
|
||||
optimized: int
|
||||
symbols: Dict[str, int]
|
||||
type: int
|
||||
varnames: List[str]
|
||||
|
||||
def __init__(self, src: str, filename: str, startstr: str) -> None: ...
|
||||
@@ -0,0 +1,12 @@
|
||||
# Source: https://hg.python.org/cpython/file/2.7/Lib/_threading_local.py
|
||||
from typing import Any
|
||||
|
||||
class _localbase(object): ...
|
||||
|
||||
class local(_localbase):
|
||||
def __getattribute__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
def __delattr__(self, name: str) -> None: ...
|
||||
def __del__(self) -> None: ...
|
||||
|
||||
def _patch(self: local) -> None: ...
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import Any, Callable, Dict, Set, Tuple, Type, TypeVar
|
||||
import _weakrefset
|
||||
|
||||
_FuncT = TypeVar('_FuncT', bound=Callable[..., Any])
|
||||
|
||||
# NOTE: mypy has special processing for ABCMeta and abstractmethod.
|
||||
|
||||
def abstractmethod(funcobj: _FuncT) -> _FuncT: ...
|
||||
|
||||
class ABCMeta(type):
|
||||
# TODO: FrozenSet
|
||||
__abstractmethods__: Set[Any]
|
||||
_abc_cache: _weakrefset.WeakSet[Any]
|
||||
_abc_invalidation_counter: int
|
||||
_abc_negative_cache: _weakrefset.WeakSet[Any]
|
||||
_abc_negative_cache_version: int
|
||||
_abc_registry: _weakrefset.WeakSet[Any]
|
||||
def __init__(self, name: str, bases: Tuple[type, ...], namespace: Dict[Any, Any]) -> None: ...
|
||||
def __instancecheck__(cls: ABCMeta, instance: Any) -> Any: ...
|
||||
def __subclasscheck__(cls: ABCMeta, subclass: Any) -> Any: ...
|
||||
def _dump_registry(cls: ABCMeta, *args: Any, **kwargs: Any) -> None: ...
|
||||
def register(cls: ABCMeta, subclass: Type[Any]) -> None: ...
|
||||
|
||||
# TODO: The real abc.abstractproperty inherits from "property".
|
||||
class abstractproperty(object):
|
||||
def __new__(cls, func: Any) -> Any: ...
|
||||
__isabstractmethod__: bool
|
||||
doc: Any
|
||||
fdel: Any
|
||||
fget: Any
|
||||
fset: Any
|
||||
@@ -0,0 +1,28 @@
|
||||
# Python 2.7 ast
|
||||
|
||||
# Rename typing to _typing, as not to conflict with typing imported
|
||||
# from _ast below when loaded in an unorthodox way by the Dropbox
|
||||
# internal Bazel integration.
|
||||
import typing as _typing
|
||||
from typing import Any, Iterator, Optional, Union
|
||||
|
||||
from _ast import *
|
||||
from _ast import AST, Module
|
||||
|
||||
def parse(source: Union[str, unicode], filename: Union[str, unicode] = ..., mode: Union[str, unicode] = ...) -> Module: ...
|
||||
def copy_location(new_node: AST, old_node: AST) -> AST: ...
|
||||
def dump(node: AST, annotate_fields: bool = ..., include_attributes: bool = ...) -> str: ...
|
||||
def fix_missing_locations(node: AST) -> AST: ...
|
||||
def get_docstring(node: AST, clean: bool = ...) -> str: ...
|
||||
def increment_lineno(node: AST, n: int = ...) -> AST: ...
|
||||
def iter_child_nodes(node: AST) -> Iterator[AST]: ...
|
||||
def iter_fields(node: AST) -> Iterator[_typing.Tuple[str, Any]]: ...
|
||||
def literal_eval(node_or_string: Union[str, unicode, AST]) -> Any: ...
|
||||
def walk(node: AST) -> Iterator[AST]: ...
|
||||
|
||||
class NodeVisitor():
|
||||
def visit(self, node: AST) -> Any: ...
|
||||
def generic_visit(self, node: AST) -> Any: ...
|
||||
|
||||
class NodeTransformer(NodeVisitor):
|
||||
def generic_visit(self, node: AST) -> Optional[AST]: ...
|
||||
@@ -0,0 +1,5 @@
|
||||
from typing import TypeVar, Any
|
||||
|
||||
_FT = TypeVar('_FT')
|
||||
|
||||
def register(func: _FT, *args: Any, **kargs: Any) -> _FT: ...
|
||||
@@ -0,0 +1,32 @@
|
||||
from typing import Any, IO, List
|
||||
|
||||
HIGHEST_PROTOCOL: int
|
||||
compatible_formats: List[str]
|
||||
format_version: str
|
||||
|
||||
class Pickler:
|
||||
def __init__(self, file: IO[str], protocol: int = ...) -> None: ...
|
||||
|
||||
def dump(self, obj: Any) -> None: ...
|
||||
|
||||
def clear_memo(self) -> None: ...
|
||||
|
||||
|
||||
class Unpickler:
|
||||
def __init__(self, file: IO[str]) -> None: ...
|
||||
|
||||
def load(self) -> Any: ...
|
||||
|
||||
def noload(self) -> Any: ...
|
||||
|
||||
|
||||
def dump(obj: Any, file: IO[str], protocol: int = ...) -> None: ...
|
||||
def dumps(obj: Any, protocol: int = ...) -> str: ...
|
||||
def load(file: IO[str]) -> Any: ...
|
||||
def loads(str: str) -> Any: ...
|
||||
|
||||
class PickleError(Exception): ...
|
||||
class UnpicklingError(PickleError): ...
|
||||
class BadPickleGet(UnpicklingError): ...
|
||||
class PicklingError(PickleError): ...
|
||||
class UnpickleableError(PicklingError): ...
|
||||
@@ -0,0 +1,54 @@
|
||||
# Stubs for cStringIO (Python 2.7)
|
||||
# See https://docs.python.org/2/library/stringio.html
|
||||
|
||||
from abc import ABCMeta
|
||||
from typing import overload, IO, List, Iterable, Iterator, Optional, Union
|
||||
from types import TracebackType
|
||||
|
||||
# TODO the typing.IO[] generics should be split into input and output.
|
||||
|
||||
# This class isn't actually abstract, but you can't instantiate it
|
||||
# directly, so we might as well treat it as abstract in the stub.
|
||||
class InputType(IO[str], Iterator[str], metaclass=ABCMeta):
|
||||
def getvalue(self) -> str: ...
|
||||
def close(self) -> None: ...
|
||||
@property
|
||||
def closed(self) -> bool: ...
|
||||
def flush(self) -> None: ...
|
||||
def isatty(self) -> bool: ...
|
||||
def read(self, size: int = ...) -> str: ...
|
||||
def readline(self, size: int = ...) -> str: ...
|
||||
def readlines(self, hint: int = ...) -> List[str]: ...
|
||||
def seek(self, offset: int, whence: int = ...) -> int: ...
|
||||
def tell(self) -> int: ...
|
||||
def truncate(self, size: Optional[int] = ...) -> int: ...
|
||||
def __iter__(self) -> InputType: ...
|
||||
def next(self) -> str: ...
|
||||
def reset(self) -> None: ...
|
||||
|
||||
|
||||
class OutputType(IO[str], Iterator[str], metaclass=ABCMeta):
|
||||
@property
|
||||
def softspace(self) -> int: ...
|
||||
def getvalue(self) -> str: ...
|
||||
def close(self) -> None: ...
|
||||
@property
|
||||
def closed(self) -> bool: ...
|
||||
def flush(self) -> None: ...
|
||||
def isatty(self) -> bool: ...
|
||||
def read(self, size: int = ...) -> str: ...
|
||||
def readline(self, size: int = ...) -> str: ...
|
||||
def readlines(self, hint: int = ...) -> List[str]: ...
|
||||
def seek(self, offset: int, whence: int = ...) -> int: ...
|
||||
def tell(self) -> int: ...
|
||||
def truncate(self, size: Optional[int] = ...) -> int: ...
|
||||
def __iter__(self) -> OutputType: ...
|
||||
def next(self) -> str: ...
|
||||
def reset(self) -> None: ...
|
||||
def write(self, b: Union[str, unicode]) -> int: ...
|
||||
def writelines(self, lines: Iterable[Union[str, unicode]]) -> None: ...
|
||||
|
||||
@overload
|
||||
def StringIO() -> OutputType: ...
|
||||
@overload
|
||||
def StringIO(s: str) -> InputType: ...
|
||||
@@ -0,0 +1,120 @@
|
||||
# These are not exported.
|
||||
from typing import Any, Dict, Generic, TypeVar, Tuple, overload, Type, Optional, List, Union, Reversible
|
||||
|
||||
# These are exported.
|
||||
from typing import (
|
||||
Callable as Callable,
|
||||
Container as Container,
|
||||
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,
|
||||
Sequence as Sequence,
|
||||
AbstractSet as Set,
|
||||
Sized as Sized,
|
||||
ValuesView as ValuesView,
|
||||
)
|
||||
|
||||
_S = TypeVar('_S')
|
||||
_T = TypeVar('_T')
|
||||
_KT = TypeVar('_KT')
|
||||
_VT = TypeVar('_VT')
|
||||
|
||||
# namedtuple is special-cased in the type checker; the initializer is ignored.
|
||||
def namedtuple(typename: Union[str, unicode], field_names: Union[str, unicode, Iterable[Union[str, unicode]]],
|
||||
verbose: bool = ..., rename: bool = ...) -> Type[Tuple[Any, ...]]: ...
|
||||
|
||||
class deque(Sized, Iterable[_T], Reversible[_T], Generic[_T]):
|
||||
def __init__(self, iterable: Iterable[_T] = ...,
|
||||
maxlen: int = ...) -> None: ...
|
||||
@property
|
||||
def maxlen(self) -> Optional[int]: ...
|
||||
def append(self, x: _T) -> None: ...
|
||||
def appendleft(self, x: _T) -> None: ...
|
||||
def clear(self) -> None: ...
|
||||
def count(self, x: _T) -> int: ...
|
||||
def extend(self, iterable: Iterable[_T]) -> None: ...
|
||||
def extendleft(self, iterable: Iterable[_T]) -> None: ...
|
||||
def pop(self) -> _T: ...
|
||||
def popleft(self) -> _T: ...
|
||||
def remove(self, value: _T) -> None: ...
|
||||
def reverse(self) -> None: ...
|
||||
def rotate(self, n: int) -> None: ...
|
||||
def __len__(self) -> int: ...
|
||||
def __iter__(self) -> Iterator[_T]: ...
|
||||
def __str__(self) -> str: ...
|
||||
def __hash__(self) -> int: ...
|
||||
def __getitem__(self, i: int) -> _T: ...
|
||||
def __setitem__(self, i: int, x: _T) -> None: ...
|
||||
def __contains__(self, o: _T) -> bool: ...
|
||||
def __reversed__(self) -> Iterator[_T]: ...
|
||||
def __iadd__(self: _S, iterable: Iterable[_T]) -> _S: ...
|
||||
|
||||
class Counter(Dict[_T, int], Generic[_T]):
|
||||
@overload
|
||||
def __init__(self, **kwargs: int) -> None: ...
|
||||
@overload
|
||||
def __init__(self, mapping: Mapping[_T, int]) -> None: ...
|
||||
@overload
|
||||
def __init__(self, iterable: Iterable[_T]) -> None: ...
|
||||
def copy(self: _S) -> _S: ...
|
||||
def elements(self) -> Iterator[_T]: ...
|
||||
def most_common(self, n: Optional[int] = ...) -> List[Tuple[_T, int]]: ...
|
||||
@overload
|
||||
def subtract(self, __mapping: Mapping[_T, int]) -> None: ...
|
||||
@overload
|
||||
def subtract(self, iterable: Iterable[_T]) -> None: ...
|
||||
# The Iterable[Tuple[...]] argument type is not actually desirable
|
||||
# (the tuples will be added as keys, breaking type safety) but
|
||||
# it's included so that the signature is compatible with
|
||||
# Dict.update. Not sure if we should use '# type: ignore' instead
|
||||
# and omit the type from the union.
|
||||
@overload
|
||||
def update(self, __m: Mapping[_T, int], **kwargs: int) -> None: ...
|
||||
@overload
|
||||
def update(self, __m: Union[Iterable[_T], Iterable[Tuple[_T, int]]], **kwargs: int) -> None: ...
|
||||
@overload
|
||||
def update(self, **kwargs: int) -> None: ...
|
||||
|
||||
def __add__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
def __sub__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
def __and__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
def __or__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
def __iadd__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
def __isub__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
def __iand__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
def __ior__(self, other: Counter[_T]) -> Counter[_T]: ...
|
||||
|
||||
class OrderedDict(Dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]):
|
||||
def popitem(self, last: bool = ...) -> Tuple[_KT, _VT]: ...
|
||||
def copy(self: _S) -> _S: ...
|
||||
def __reversed__(self) -> Iterator[_KT]: ...
|
||||
|
||||
class defaultdict(Dict[_KT, _VT], Generic[_KT, _VT]):
|
||||
default_factory: Callable[[], _VT]
|
||||
@overload
|
||||
def __init__(self, **kwargs: _VT) -> None: ...
|
||||
@overload
|
||||
def __init__(self, default_factory: Optional[Callable[[], _VT]]) -> None: ...
|
||||
@overload
|
||||
def __init__(self, default_factory: Optional[Callable[[], _VT]], **kwargs: _VT) -> None: ...
|
||||
@overload
|
||||
def __init__(self, default_factory: Optional[Callable[[], _VT]],
|
||||
map: Mapping[_KT, _VT]) -> None: ...
|
||||
@overload
|
||||
def __init__(self, default_factory: Optional[Callable[[], _VT]],
|
||||
map: Mapping[_KT, _VT], **kwargs: _VT) -> None: ...
|
||||
@overload
|
||||
def __init__(self, default_factory: Optional[Callable[[], _VT]],
|
||||
iterable: Iterable[Tuple[_KT, _VT]]) -> None: ...
|
||||
@overload
|
||||
def __init__(self, default_factory: Optional[Callable[[], _VT]],
|
||||
iterable: Iterable[Tuple[_KT, _VT]], **kwargs: _VT) -> None: ...
|
||||
def __missing__(self, key: _KT) -> _VT: ...
|
||||
def copy(self: _S) -> _S: ...
|
||||
@@ -0,0 +1,12 @@
|
||||
from typing import overload, AnyStr, Text, Tuple
|
||||
|
||||
def getstatus(file: Text) -> str: ...
|
||||
def getoutput(cmd: Text) -> str: ...
|
||||
def getstatusoutput(cmd: Text) -> Tuple[int, str]: ...
|
||||
|
||||
@overload
|
||||
def mk2arg(head: bytes, x: bytes) -> bytes: ...
|
||||
@overload
|
||||
def mk2arg(head: Text, x: Text) -> Text: ...
|
||||
|
||||
def mkarg(x: AnyStr) -> AnyStr: ...
|
||||
@@ -0,0 +1,19 @@
|
||||
# Stubs for compileall (Python 2)
|
||||
|
||||
from typing import Any, Optional, Pattern, Union
|
||||
|
||||
_Path = Union[str, bytes]
|
||||
|
||||
# rx can be any object with a 'search' method; once we have Protocols we can change the type
|
||||
def compile_dir(
|
||||
dir: _Path,
|
||||
maxlevels: int = ...,
|
||||
ddir: Optional[_Path] = ...,
|
||||
force: bool = ...,
|
||||
rx: Optional[Pattern[Any]] = ...,
|
||||
quiet: int = ...,
|
||||
) -> int: ...
|
||||
def compile_file(
|
||||
fullname: _Path, ddir: Optional[_Path] = ..., force: bool = ..., rx: Optional[Pattern[Any]] = ..., quiet: int = ...,
|
||||
) -> int: ...
|
||||
def compile_path(skip_curdir: bool = ..., maxlevels: int = ..., force: bool = ..., quiet: int = ...) -> int: ...
|
||||
@@ -0,0 +1,112 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
class Cookie:
|
||||
version: Any
|
||||
name: Any
|
||||
value: Any
|
||||
port: Any
|
||||
port_specified: Any
|
||||
domain: Any
|
||||
domain_specified: Any
|
||||
domain_initial_dot: Any
|
||||
path: Any
|
||||
path_specified: Any
|
||||
secure: Any
|
||||
expires: Any
|
||||
discard: Any
|
||||
comment: Any
|
||||
comment_url: Any
|
||||
rfc2109: Any
|
||||
def __init__(self, version, name, value, port, port_specified, domain, domain_specified, domain_initial_dot, path,
|
||||
path_specified, secure, expires, discard, comment, comment_url, rest, rfc2109: bool = ...): ...
|
||||
def has_nonstandard_attr(self, name): ...
|
||||
def get_nonstandard_attr(self, name, default: Optional[Any] = ...): ...
|
||||
def set_nonstandard_attr(self, name, value): ...
|
||||
def is_expired(self, now: Optional[Any] = ...): ...
|
||||
|
||||
class CookiePolicy:
|
||||
def set_ok(self, cookie, request): ...
|
||||
def return_ok(self, cookie, request): ...
|
||||
def domain_return_ok(self, domain, request): ...
|
||||
def path_return_ok(self, path, request): ...
|
||||
|
||||
class DefaultCookiePolicy(CookiePolicy):
|
||||
DomainStrictNoDots: Any
|
||||
DomainStrictNonDomain: Any
|
||||
DomainRFC2965Match: Any
|
||||
DomainLiberal: Any
|
||||
DomainStrict: Any
|
||||
netscape: Any
|
||||
rfc2965: Any
|
||||
rfc2109_as_netscape: Any
|
||||
hide_cookie2: Any
|
||||
strict_domain: Any
|
||||
strict_rfc2965_unverifiable: Any
|
||||
strict_ns_unverifiable: Any
|
||||
strict_ns_domain: Any
|
||||
strict_ns_set_initial_dollar: Any
|
||||
strict_ns_set_path: Any
|
||||
def __init__(self, blocked_domains: Optional[Any] = ..., allowed_domains: Optional[Any] = ..., netscape: bool = ...,
|
||||
rfc2965: bool = ..., rfc2109_as_netscape: Optional[Any] = ..., hide_cookie2: bool = ...,
|
||||
strict_domain: bool = ..., strict_rfc2965_unverifiable: bool = ..., strict_ns_unverifiable: bool = ...,
|
||||
strict_ns_domain=..., strict_ns_set_initial_dollar: bool = ..., strict_ns_set_path: bool = ...): ...
|
||||
def blocked_domains(self): ...
|
||||
def set_blocked_domains(self, blocked_domains): ...
|
||||
def is_blocked(self, domain): ...
|
||||
def allowed_domains(self): ...
|
||||
def set_allowed_domains(self, allowed_domains): ...
|
||||
def is_not_allowed(self, domain): ...
|
||||
def set_ok(self, cookie, request): ...
|
||||
def set_ok_version(self, cookie, request): ...
|
||||
def set_ok_verifiability(self, cookie, request): ...
|
||||
def set_ok_name(self, cookie, request): ...
|
||||
def set_ok_path(self, cookie, request): ...
|
||||
def set_ok_domain(self, cookie, request): ...
|
||||
def set_ok_port(self, cookie, request): ...
|
||||
def return_ok(self, cookie, request): ...
|
||||
def return_ok_version(self, cookie, request): ...
|
||||
def return_ok_verifiability(self, cookie, request): ...
|
||||
def return_ok_secure(self, cookie, request): ...
|
||||
def return_ok_expires(self, cookie, request): ...
|
||||
def return_ok_port(self, cookie, request): ...
|
||||
def return_ok_domain(self, cookie, request): ...
|
||||
def domain_return_ok(self, domain, request): ...
|
||||
def path_return_ok(self, path, request): ...
|
||||
|
||||
class Absent: ...
|
||||
|
||||
class CookieJar:
|
||||
non_word_re: Any
|
||||
quote_re: Any
|
||||
strict_domain_re: Any
|
||||
domain_re: Any
|
||||
dots_re: Any
|
||||
magic_re: Any
|
||||
def __init__(self, policy: Optional[Any] = ...): ...
|
||||
def set_policy(self, policy): ...
|
||||
def add_cookie_header(self, request): ...
|
||||
def make_cookies(self, response, request): ...
|
||||
def set_cookie_if_ok(self, cookie, request): ...
|
||||
def set_cookie(self, cookie): ...
|
||||
def extract_cookies(self, response, request): ...
|
||||
def clear(self, domain: Optional[Any] = ..., path: Optional[Any] = ..., name: Optional[Any] = ...): ...
|
||||
def clear_session_cookies(self): ...
|
||||
def clear_expired_cookies(self): ...
|
||||
def __iter__(self): ...
|
||||
def __len__(self): ...
|
||||
|
||||
class LoadError(IOError): ...
|
||||
|
||||
class FileCookieJar(CookieJar):
|
||||
filename: Any
|
||||
delayload: Any
|
||||
def __init__(self, filename: Optional[Any] = ..., delayload: bool = ..., policy: Optional[Any] = ...): ...
|
||||
def save(self, filename: Optional[Any] = ..., ignore_discard: bool = ..., ignore_expires: bool = ...): ...
|
||||
def load(self, filename: Optional[Any] = ..., ignore_discard: bool = ..., ignore_expires: bool = ...): ...
|
||||
def revert(self, filename: Optional[Any] = ..., ignore_discard: bool = ..., ignore_expires: bool = ...): ...
|
||||
|
||||
class LWPCookieJar(FileCookieJar):
|
||||
def as_lwp_str(self, ignore_discard: bool = ..., ignore_expires: bool = ...) -> str: ... # undocumented
|
||||
|
||||
MozillaCookieJar = FileCookieJar
|
||||
def lwp_cookie_str(cookie: Cookie) -> str: ...
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
from typing import TypeVar, Callable, Union, Tuple, Any, Optional, SupportsInt, Hashable, List
|
||||
|
||||
|
||||
_Type = TypeVar("_Type", bound=type)
|
||||
_Reduce = Union[Tuple[Callable[..., _Type], Tuple[Any, ...]], Tuple[Callable[..., _Type], Tuple[Any, ...], Optional[Any]]]
|
||||
|
||||
__all__: List[str]
|
||||
|
||||
def pickle(ob_type: _Type, pickle_function: Callable[[_Type], Union[str, _Reduce[_Type]]], constructor_ob: Optional[Callable[[_Reduce[_Type]], _Type]] = ...) -> None: ...
|
||||
def constructor(object: Callable[[_Reduce[_Type]], _Type]) -> None: ...
|
||||
def add_extension(module: Hashable, name: Hashable, code: SupportsInt) -> None: ...
|
||||
def remove_extension(module: Hashable, name: Hashable, code: int) -> None: ...
|
||||
def clear_extension_cache() -> None: ...
|
||||
@@ -0,0 +1,10 @@
|
||||
# Source: https://hg.python.org/cpython/file/2.7/Lib/dircache.py
|
||||
|
||||
from typing import List, MutableSequence, Text, Union
|
||||
|
||||
def reset() -> None: ...
|
||||
def listdir(path: Text) -> List[str]: ...
|
||||
|
||||
opendir = listdir
|
||||
|
||||
def annotate(head: Text, list: Union[MutableSequence[str], MutableSequence[Text], MutableSequence[Union[str, Text]]]) -> None: ...
|
||||
@@ -0,0 +1,5 @@
|
||||
# Stubs for emxccompiler
|
||||
|
||||
from distutils.unixccompiler import UnixCCompiler
|
||||
|
||||
class EMXCCompiler(UnixCCompiler): ...
|
||||
@@ -0,0 +1,21 @@
|
||||
from typing import Any, Callable, Dict, NoReturn, Optional, Tuple
|
||||
|
||||
class error(Exception):
|
||||
def __init__(self, *args: Any) -> None: ...
|
||||
|
||||
def start_new_thread(function: Callable[..., Any], args: Tuple[Any, ...], kwargs: Dict[str, Any] = ...) -> None: ...
|
||||
def exit() -> NoReturn: ...
|
||||
def get_ident() -> int: ...
|
||||
def allocate_lock() -> LockType: ...
|
||||
def stack_size(size: Optional[int] = ...) -> int: ...
|
||||
|
||||
class LockType(object):
|
||||
locked_status: bool
|
||||
def __init__(self) -> None: ...
|
||||
def acquire(self, waitflag: Optional[bool] = ...) -> bool: ...
|
||||
def __enter__(self, waitflag: Optional[bool] = ...) -> bool: ...
|
||||
def __exit__(self, typ: Any, val: Any, tb: Any) -> None: ...
|
||||
def release(self) -> bool: ...
|
||||
def locked(self) -> bool: ...
|
||||
|
||||
def interrupt_main() -> None: ...
|
||||
@@ -0,0 +1,4 @@
|
||||
from email.mime.nonmultipart import MIMENonMultipart
|
||||
|
||||
class MIMEText(MIMENonMultipart):
|
||||
def __init__(self, _text, _subtype=..., _charset=...) -> None: ...
|
||||
@@ -0,0 +1,6 @@
|
||||
from typing import IO, Any, AnyStr
|
||||
|
||||
def message_from_string(s: AnyStr, *args, **kwargs): ...
|
||||
def message_from_bytes(s: str, *args, **kwargs): ...
|
||||
def message_from_file(fp: IO[AnyStr], *args, **kwargs): ...
|
||||
def message_from_binary_file(fp: IO[str], *args, **kwargs): ...
|
||||
@@ -0,0 +1,40 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
def parsedate_tz(data): ...
|
||||
def parsedate(data): ...
|
||||
def mktime_tz(data): ...
|
||||
def quote(str): ...
|
||||
|
||||
class AddrlistClass:
|
||||
specials: Any
|
||||
pos: Any
|
||||
LWS: Any
|
||||
CR: Any
|
||||
FWS: Any
|
||||
atomends: Any
|
||||
phraseends: Any
|
||||
field: Any
|
||||
commentlist: Any
|
||||
def __init__(self, field): ...
|
||||
def gotonext(self): ...
|
||||
def getaddrlist(self): ...
|
||||
def getaddress(self): ...
|
||||
def getrouteaddr(self): ...
|
||||
def getaddrspec(self): ...
|
||||
def getdomain(self): ...
|
||||
def getdelimited(self, beginchar, endchars, allowcomments: bool = ...): ...
|
||||
def getquote(self): ...
|
||||
def getcomment(self): ...
|
||||
def getdomainliteral(self): ...
|
||||
def getatom(self, atomends: Optional[Any] = ...): ...
|
||||
def getphraselist(self): ...
|
||||
|
||||
class AddressList(AddrlistClass):
|
||||
addresslist: Any
|
||||
def __init__(self, field): ...
|
||||
def __len__(self): ...
|
||||
def __add__(self, other): ...
|
||||
def __iadd__(self, other): ...
|
||||
def __sub__(self, other): ...
|
||||
def __isub__(self, other): ...
|
||||
def __getitem__(self, index): ...
|
||||
@@ -0,0 +1,8 @@
|
||||
def base64_len(s: bytes) -> int: ...
|
||||
def header_encode(header, charset=..., keep_eols=..., maxlinelen=..., eol=...): ...
|
||||
def encode(s, binary=..., maxlinelen=..., eol=...): ...
|
||||
body_encode = encode
|
||||
encodestring = encode
|
||||
def decode(s, convert_eols=...): ...
|
||||
body_decode = decode
|
||||
decodestring = decode
|
||||
@@ -0,0 +1,26 @@
|
||||
def add_charset(charset, header_enc=..., body_enc=..., output_charset=...) -> None: ...
|
||||
def add_alias(alias, canonical) -> None: ...
|
||||
def add_codec(charset, codecname) -> None: ...
|
||||
|
||||
QP: int # undocumented
|
||||
BASE64: int # undocumented
|
||||
SHORTEST: int # undocumented
|
||||
|
||||
class Charset:
|
||||
input_charset = ...
|
||||
header_encoding = ...
|
||||
body_encoding = ...
|
||||
output_charset = ...
|
||||
input_codec = ...
|
||||
output_codec = ...
|
||||
def __init__(self, input_charset=...) -> None: ...
|
||||
def __eq__(self, other): ...
|
||||
def __ne__(self, other): ...
|
||||
def get_body_encoding(self): ...
|
||||
def convert(self, s): ...
|
||||
def to_splittable(self, s): ...
|
||||
def from_splittable(self, ustr, to_output: bool = ...): ...
|
||||
def get_output_charset(self): ...
|
||||
def encoded_header_len(self, s): ...
|
||||
def header_encode(self, s, convert: bool = ...): ...
|
||||
def body_encode(self, s, convert: bool = ...): ...
|
||||
@@ -0,0 +1,4 @@
|
||||
def encode_base64(msg) -> None: ...
|
||||
def encode_quopri(msg) -> None: ...
|
||||
def encode_7or8bit(msg) -> None: ...
|
||||
def encode_noop(msg) -> None: ...
|
||||
@@ -0,0 +1,18 @@
|
||||
class BufferedSubFile:
|
||||
def __init__(self) -> None: ...
|
||||
def push_eof_matcher(self, pred) -> None: ...
|
||||
def pop_eof_matcher(self): ...
|
||||
def close(self) -> None: ...
|
||||
def readline(self): ...
|
||||
def unreadline(self, line) -> None: ...
|
||||
def push(self, data): ...
|
||||
def pushlines(self, lines) -> None: ...
|
||||
def is_closed(self): ...
|
||||
def __iter__(self): ...
|
||||
def next(self): ...
|
||||
|
||||
|
||||
class FeedParser:
|
||||
def __init__(self, _factory=...) -> None: ...
|
||||
def feed(self, data) -> None: ...
|
||||
def close(self): ...
|
||||
@@ -0,0 +1,9 @@
|
||||
class Generator:
|
||||
def __init__(self, outfp, mangle_from_: bool = ..., maxheaderlen: int = ...) -> None: ...
|
||||
def write(self, s) -> None: ...
|
||||
def flatten(self, msg, unixfrom: bool = ...) -> None: ...
|
||||
def clone(self, fp): ...
|
||||
|
||||
|
||||
class DecodedGenerator(Generator):
|
||||
def __init__(self, outfp, mangle_from_: bool = ..., maxheaderlen: int = ..., fmt=...) -> None: ...
|
||||
@@ -0,0 +1,11 @@
|
||||
def decode_header(header): ...
|
||||
def make_header(decoded_seq, maxlinelen=..., header_name=..., continuation_ws=...): ...
|
||||
|
||||
class Header:
|
||||
def __init__(self, s=..., charset=..., maxlinelen=..., header_name=..., continuation_ws=...,
|
||||
errors=...) -> None: ...
|
||||
def __unicode__(self): ...
|
||||
def __eq__(self, other): ...
|
||||
def __ne__(self, other): ...
|
||||
def append(self, s, charset=..., errors=...) -> None: ...
|
||||
def encode(self, splitchars=...): ...
|
||||
@@ -0,0 +1,5 @@
|
||||
from typing import Any, Generator
|
||||
|
||||
def walk(self) -> Generator[Any, Any, Any]: ...
|
||||
def body_line_iterator(msg, decode: bool = ...) -> Generator[Any, Any, Any]: ...
|
||||
def typed_subpart_iterator(msg, maintype=..., subtype=...) -> Generator[Any, Any, Any]: ...
|
||||
@@ -0,0 +1,45 @@
|
||||
from typing import Any, Generator
|
||||
|
||||
class Message:
|
||||
preamble = ...
|
||||
epilogue = ...
|
||||
defects = ...
|
||||
def __init__(self): ...
|
||||
def as_string(self, unixfrom=...): ...
|
||||
def is_multipart(self) -> bool: ...
|
||||
def set_unixfrom(self, unixfrom) -> None: ...
|
||||
def get_unixfrom(self): ...
|
||||
def attach(self, payload) -> None: ...
|
||||
def get_payload(self, i=..., decode: bool = ...): ...
|
||||
def set_payload(self, payload, charset=...) -> None: ...
|
||||
def set_charset(self, charset): ...
|
||||
def get_charset(self): ...
|
||||
def __len__(self): ...
|
||||
def __getitem__(self, name): ...
|
||||
def __setitem__(self, name, val) -> None: ...
|
||||
def __delitem__(self, name) -> None: ...
|
||||
def __contains__(self, name): ...
|
||||
def has_key(self, name) -> bool: ...
|
||||
def keys(self): ...
|
||||
def values(self): ...
|
||||
def items(self): ...
|
||||
def get(self, name, failobj=...): ...
|
||||
def get_all(self, name, failobj=...): ...
|
||||
def add_header(self, _name, _value, **_params) -> None: ...
|
||||
def replace_header(self, _name, _value) -> None: ...
|
||||
def get_content_type(self): ...
|
||||
def get_content_maintype(self): ...
|
||||
def get_content_subtype(self): ...
|
||||
def get_default_type(self): ...
|
||||
def set_default_type(self, ctype) -> None: ...
|
||||
def get_params(self, failobj=..., header=..., unquote: bool = ...): ...
|
||||
def get_param(self, param, failobj=..., header=..., unquote: bool = ...): ...
|
||||
def set_param(self, param, value, header=..., requote: bool = ..., charset=..., language=...) -> None: ...
|
||||
def del_param(self, param, header=..., requote: bool = ...): ...
|
||||
def set_type(self, type, header=..., requote: bool = ...): ...
|
||||
def get_filename(self, failobj=...): ...
|
||||
def get_boundary(self, failobj=...): ...
|
||||
def set_boundary(self, boundary) -> None: ...
|
||||
def get_content_charset(self, failobj=...): ...
|
||||
def get_charsets(self, failobj=...): ...
|
||||
def walk(self) -> Generator[Any, Any, Any]: ...
|
||||
@@ -0,0 +1,11 @@
|
||||
# Stubs for email.mime.application
|
||||
|
||||
from typing import Callable, Optional, Tuple, Union
|
||||
from email.mime.nonmultipart import MIMENonMultipart
|
||||
|
||||
_ParamsType = Union[str, None, Tuple[str, Optional[str], str]]
|
||||
|
||||
class MIMEApplication(MIMENonMultipart):
|
||||
def __init__(self, _data: bytes, _subtype: str = ...,
|
||||
_encoder: Callable[[MIMEApplication], None] = ...,
|
||||
**_params: _ParamsType) -> None: ...
|
||||
@@ -0,0 +1,5 @@
|
||||
from email.mime.nonmultipart import MIMENonMultipart
|
||||
|
||||
|
||||
class MIMEAudio(MIMENonMultipart):
|
||||
def __init__(self, _audiodata, _subtype=..., _encoder=..., **_params) -> None: ...
|
||||
@@ -0,0 +1,4 @@
|
||||
from email import message
|
||||
|
||||
class MIMEBase(message.Message):
|
||||
def __init__(self, _maintype, _subtype, **_params) -> None: ...
|
||||
@@ -0,0 +1,5 @@
|
||||
from email.mime.nonmultipart import MIMENonMultipart
|
||||
|
||||
|
||||
class MIMEImage(MIMENonMultipart):
|
||||
def __init__(self, _imagedata, _subtype=..., _encoder=..., **_params) -> None: ...
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user