diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index be8582a6b221d..8d89ec6d60434 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -6,3 +6,7 @@ f98f78216ba9d6ab68c8e69c19e9f3c7926c5efe # run pyupgrade (#12711) fc335cb16315964b923eb1927e3aad1516891c28 +# update black to 23.3.0 (#15059) +4276308be01ea498d946a79554b4a10b1cf13ccb +# Update black to 24.1.1 (#16847) +8107e53158d83d30bb04d290ac10d8d3ccd344f8 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f13a3de1f2e33..112102954dd3b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,6 +28,7 @@ concurrency: jobs: docs: runs-on: ubuntu-latest + timeout-minutes: 10 env: TOXENV: docs TOX_SKIP_MISSING_INTERPRETERS: False @@ -36,9 +37,9 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.12' - name: Install tox - run: pip install --upgrade 'setuptools!=50' tox==4.11.0 + run: pip install tox==4.21.2 - name: Setup tox environment run: tox run -e ${{ env.TOXENV }} --notest - name: Test diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 07a1d0863eb24..2b2327798a721 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -32,6 +32,7 @@ jobs: matrix: shard-index: [0, 1, 2, 3, 4] fail-fast: false + timeout-minutes: 60 steps: - uses: actions/checkout@v4 with: @@ -39,7 +40,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.12" - name: Install dependencies run: | python -m pip install -U pip @@ -69,18 +70,35 @@ jobs: --output concise \ | tee diff_${{ matrix.shard-index }}.txt ) || [ $? -eq 1 ] - - name: Upload mypy_primer diff - uses: actions/upload-artifact@v3 - with: - name: mypy_primer_diffs - path: diff_${{ matrix.shard-index }}.txt - - if: ${{ matrix.shard-index }} == 0 + - if: ${{ matrix.shard-index == 0 }} name: Save PR number run: | echo ${{ github.event.pull_request.number }} | tee pr_number.txt - - if: ${{ matrix.shard-index }} == 0 - name: Upload PR number - uses: actions/upload-artifact@v3 + - if: ${{ matrix.shard-index == 0 }} + name: Upload mypy_primer diff + PR number + uses: actions/upload-artifact@v4 + 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@v4 + 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@v4 with: name: mypy_primer_diffs - path: pr_number.txt + pattern: mypy_primer_diffs-* + delete-merged: true diff --git a/.github/workflows/mypy_primer_comment.yml b/.github/workflows/mypy_primer_comment.yml index 492e03aff16eb..6e62d8c51713d 100644 --- a/.github/workflows/mypy_primer_comment.yml +++ b/.github/workflows/mypy_primer_comment.yml @@ -18,7 +18,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download diffs - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const fs = require('fs'); diff --git a/.github/workflows/sync_typeshed.yml b/.github/workflows/sync_typeshed.yml index b545e7b0662b2..84d246441f3d4 100644 --- a/.github/workflows/sync_typeshed.yml +++ b/.github/workflows/sync_typeshed.yml @@ -14,6 +14,7 @@ jobs: name: Sync typeshed if: github.repository == 'python/mypy' runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 98a737a78b3b2..9e6c9cd1d9b32 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,6 +70,22 @@ jobs: toxenv: py tox_extra_args: "-n 4" test_mypyc: true + - name: Test suite with py313-ubuntu, mypyc-compiled + python: '3.13' + arch: x64 + os: ubuntu-latest + toxenv: py + tox_extra_args: "-n 4" + test_mypyc: true + + # - name: Test suite with py314-dev-ubuntu + # python: '3.14-dev' + # arch: x64 + # os: ubuntu-latest + # toxenv: py + # tox_extra_args: "-n 4" + # allow_failure: true + # test_mypyc: true - name: mypyc runtime tests with py39-macos python: '3.9.18' @@ -78,13 +94,16 @@ jobs: os: macos-13 toxenv: py tox_extra_args: "-n 3 mypyc/test/test_run.py mypyc/test/test_external.py" - - name: mypyc runtime tests with py38-debug-build-ubuntu - python: '3.8.17' - arch: x64 - os: ubuntu-latest - toxenv: py - tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" - debug_build: true + # This is broken. See + # - https://github.com/python/mypy/issues/17819 + # - https://github.com/python/mypy/pull/17822 + # - name: mypyc runtime tests with py38-debug-build-ubuntu + # python: '3.8.17' + # arch: x64 + # os: ubuntu-latest + # toxenv: py + # tox_extra_args: "-n 4 mypyc/test/test_run.py mypyc/test/test_external.py" + # debug_build: true - name: Type check our own code (py38-ubuntu) python: '3.8' @@ -107,10 +126,11 @@ jobs: toxenv: lint name: ${{ matrix.name }} + timeout-minutes: 60 env: TOX_SKIP_MISSING_INTERPRETERS: False - # Rich (pip) - FORCE_COLOR: 1 + # Rich (pip) -- Disable color for windows + pytest + FORCE_COLOR: ${{ !(startsWith(matrix.os, 'windows-') && startsWith(matrix.toxenv, 'py')) && 1 || 0 }} # Tox PY_COLORS: 1 # Mypy (see https://github.com/python/mypy/issues/7771) @@ -119,12 +139,10 @@ jobs: MYPY_FORCE_TERMINAL_WIDTH: 200 # Pytest PYTEST_ADDOPTS: --color=yes + steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - architecture: ${{ matrix.arch }} + - name: Debug build if: ${{ matrix.debug_build }} run: | @@ -132,24 +150,63 @@ jobs: PYTHONDIR=~/python-debug/python-$PYTHONVERSION VENV=$PYTHONDIR/env ./misc/build-debug-python.sh $PYTHONVERSION $PYTHONDIR $VENV + # TODO: does this do anything? env vars aren't passed to the next step right source $VENV/bin/activate + - name: Latest dev build + if: ${{ endsWith(matrix.python, '-dev') }} + run: | + git clone --depth 1 https://github.com/python/cpython.git /tmp/cpython --branch $( echo ${{ matrix.python }} | sed 's/-dev//' ) + cd /tmp/cpython + echo git rev-parse HEAD; git rev-parse HEAD + git show --no-patch + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential gdb lcov libbz2-dev libffi-dev libgdbm-dev liblzma-dev libncurses5-dev \ + libreadline6-dev libsqlite3-dev libssl-dev lzma lzma-dev tk-dev uuid-dev zlib1g-dev + ./configure --prefix=/opt/pythondev + make -j$(nproc) + sudo make install + sudo ln -s /opt/pythondev/bin/python3 /opt/pythondev/bin/python + sudo ln -s /opt/pythondev/bin/pip3 /opt/pythondev/bin/pip + echo "/opt/pythondev/bin" >> $GITHUB_PATH + - uses: actions/setup-python@v5 + if: ${{ !(matrix.debug_build || endsWith(matrix.python, '-dev')) }} + with: + python-version: ${{ matrix.python }} + architecture: ${{ matrix.arch }} + - name: Install tox - run: pip install setuptools==68.2.2 tox==4.11.0 + run: | + echo PATH; echo $PATH + echo which python; which python + echo which pip; which pip + echo python version; python -c 'import sys; print(sys.version)' + echo debug build; python -c 'import sysconfig; print(bool(sysconfig.get_config_var("Py_DEBUG")))' + echo os.cpu_count; python -c 'import os; print(os.cpu_count())' + echo os.sched_getaffinity; python -c 'import os; print(len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))' + pip install setuptools==75.1.0 tox==4.21.2 + - name: Compiled with mypyc if: ${{ matrix.test_mypyc }} run: | pip install -r test-requirements.txt CC=clang MYPYC_OPT_LEVEL=0 MYPY_USE_MYPYC=1 pip install -e . + - name: Setup tox environment run: | tox run -e ${{ matrix.toxenv }} --notest - python -c 'import os; print("os.cpu_count", os.cpu_count(), "os.sched_getaffinity", len(getattr(os, "sched_getaffinity", lambda *args: [])(0)))' - name: Test run: tox run -e ${{ matrix.toxenv }} --skip-pkg-install -- ${{ matrix.tox_extra_args }} + continue-on-error: ${{ matrix.allow_failure == 'true' }} + + - name: Mark as success (check failures manually) + if: ${{ matrix.allow_failure == 'true' }} + run: exit 0 python_32bits: runs-on: ubuntu-latest name: Test mypyc suite with 32-bit Python + timeout-minutes: 60 env: TOX_SKIP_MISSING_INTERPRETERS: False # Rich (pip) @@ -188,7 +245,7 @@ jobs: default: 3.11.1 command: python -c "import platform; print(f'{platform.architecture()=} {platform.machine()=}');" - name: Install tox - run: pip install setuptools==68.2.2 tox==4.11.0 + run: pip install setuptools==75.1.0 tox==4.21.2 - name: Setup tox environment run: tox run -e py --notest - name: Test diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 519f63ac2bd70..0652702a0fc0f 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -25,6 +25,7 @@ jobs: # Check stub file generation for a small pybind11 project # (full text match is required to pass) runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a7ff48051aad7..d8e66ecb4dfc6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,19 +1,33 @@ exclude: '^(mypyc/external/)|(mypy/typeshed/)|misc/typeshed_patches' # Exclude all vendored code from lints repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 # must match test-requirements.txt + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.1.1 # must match test-requirements.txt + rev: 24.8.0 hooks: - id: black exclude: '^(test-data/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 # must match test-requirements.txt + rev: v0.6.9 hooks: - id: ruff args: [--exit-non-zero-on-fix] + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.29.4 + hooks: + - id: check-dependabot + - id: check-github-workflows + - repo: https://github.com/rhysd/actionlint + rev: v1.7.3 + hooks: + - id: actionlint + args: [ + -ignore=property "debug_build" is not defined, + -ignore=property "allow_failure" is not defined, + -ignore=SC2(046|086), + ] ci: autoupdate_schedule: quarterly diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d5919cafe33f..01c3ed16ddbb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,889 @@ ## Next release +... + +## Mypy 1.14 + +We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). +Mypy is a static type checker for Python. This release includes new features and bug fixes. +You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +### Change to Enum Membership Semantics + +As per the updated [typing specification for enums](https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members), +enum members must be left unannotated. + +```python +class Pet(Enum): + CAT = 1 # Member attribute + DOG = 2 # Member attribute + + # New error: Enum members must be left unannotated + WOLF: int = 3 + + species: str # Considered a non-member attribute +``` + +In particular, the specification change can result in issues in type stubs (`.pyi` files), since +historically it was common to leave the value absent: + +```python +# In a type stub (.pyi file) + +class Pet(Enum): + # Change in semantics: previously considered members, + # now non-member attributes + CAT: int + DOG: int + + # Mypy will now issue a warning if it detects this + # situation in type stubs: + # > Detected enum "Pet" in a type stub with zero + # > members. There is a chance this is due to a recent + # > change in the semantics of enum membership. If so, + # > use `member = value` to mark an enum member, + # > instead of `member: type` + +class Pet(Enum): + # As per the specification, you should now do one of + # the following: + DOG = 1 # Member attribute with value 1 and known type + WOLF = cast(int, ...) # Member attribute with unknown + # value but known type + LION = ... # Member attribute with unknown value and + # # unknown type +``` + +Contributed by Terence Honles (PR [17207](https://github.com/python/mypy/pull/17207)) and +Shantanu Jain (PR [18068](https://github.com/python/mypy/pull/18068)). + +### Support for @deprecated Decorator (PEP 702) + +Mypy can now issue errors or notes when code imports a deprecated feature +explicitly with a `from mod import depr` statement, or uses a deprecated feature +imported otherwise or defined locally. Features are considered deprecated when +decorated with `warnings.deprecated`, as specified in [PEP 702](https://peps.python.org/pep-0702). + +You can enable the error code via `--enable-error-code=deprecated` on the mypy +command line or `enable_error_code = deprecated` in the mypy config file. +Use the command line flag `--report-deprecated-as-note` or config file option +`report_deprecated_as_note=True` to turn all such errors into notes. + +Deprecation errors will be enabled by default in a future mypy version. + +This feature was contributed by Christoph Tyralla. + +List of changes: + + * Add basic support for PEP 702 (`@deprecated`) (Christoph Tyralla, PR [17476](https://github.com/python/mypy/pull/17476)) + * Support descriptors with `@deprecated` (Christoph Tyralla, PR [18090](https://github.com/python/mypy/pull/18090)) + * Make "deprecated" note an error, disabled by default (Valentin Stanciu, PR [18192](https://github.com/python/mypy/pull/18192)) + * Consider all possible type positions with `@deprecated` (Christoph Tyralla, PR [17926](https://github.com/python/mypy/pull/17926)) + * Improve the handling of explicit type annotations in assignment statements with `@deprecated` (Christoph Tyralla, PR [17899](https://github.com/python/mypy/pull/17899)) + +### Optionally Analyzing Untyped Modules + +Mypy normally doesn't analyze imports from third-party modules (installed using pip, for example) +if there are no stubs or a py.typed marker file. To force mypy to analyze these imports, you +can now use the `--follow-untyped-imports` flag or set the `follow_untyped_imports` +config file option to True. This can be set either in the global section of your mypy config +file, or individually on a per-module basis. + +This feature was contributed by Jannick Kremer. + +List of changes: + + * Implement flag to allow type checking of untyped modules (Jannick Kremer, PR [17712](https://github.com/python/mypy/pull/17712)) + * Warn about `--follow-untyped-imports` (Shantanu, PR [18249](https://github.com/python/mypy/pull/18249)) + +### Support New Style Type Variable Defaults (PEP 696) + +Mypy now supports type variable defaults using the new syntax described in PEP 696, which +was introduced in Python 3.13. Example: + +```python +@dataclass +class Box[T = int]: # Set default for "T" + value: T | None = None + +reveal_type(Box()) # type is Box[int], since it's the default +reveal_type(Box(value="Hello World!")) # type is Box[str] +``` + +This feature was contributed by Marc Mueller (PR [17985](https://github.com/python/mypy/pull/17985)). + +### Improved For Loop Index Variable Type Narrowing + +Mypy now preserves the literal type of for loop index variables, to support `TypedDict` +lookups. Example: + +```python +from typing import TypedDict + +class X(TypedDict): + hourly: int + daily: int + +def func(x: X) -> int: + s = 0 + for var in ("hourly", "daily"): + # "Union[Literal['hourly']?, Literal['daily']?]" + reveal_type(var) + + # x[var] no longer triggers a literal-required error + s += x[var] + return s +``` + +This was contributed by Marc Mueller (PR [18014](https://github.com/python/mypy/pull/18014)). + +### Mypyc Improvements + + * Document optimized bytes operations and additional str operations (Jukka Lehtosalo, PR [18242](https://github.com/python/mypy/pull/18242)) + * Add primitives and specialization for `ord()` (Jukka Lehtosalo, PR [18240](https://github.com/python/mypy/pull/18240)) + * Optimize `str.encode` with specializations for common used encodings (Valentin Stanciu, PR [18232](https://github.com/python/mypy/pull/18232)) + * Fix fall back to generic operation for staticmethod and classmethod (Advait Dixit, PR [18228](https://github.com/python/mypy/pull/18228)) + * Support unicode surrogates in string literals (Jukka Lehtosalo, PR [18209](https://github.com/python/mypy/pull/18209)) + * Fix index variable in for loop with `builtins.enumerate` (Advait Dixit, PR [18202](https://github.com/python/mypy/pull/18202)) + * Fix check for enum classes (Advait Dixit, PR [18178](https://github.com/python/mypy/pull/18178)) + * Fix loading type from imported modules (Advait Dixit, PR [18158](https://github.com/python/mypy/pull/18158)) + * Fix initializers of final attributes in class body (Jared Hance, PR [18031](https://github.com/python/mypy/pull/18031)) + * Fix name generation for modules with similar full names (aatle, PR [18001](https://github.com/python/mypy/pull/18001)) + * Fix relative imports in `__init__.py` (Shantanu, PR [17979](https://github.com/python/mypy/pull/17979)) + * Optimize dunder methods (jairov4, PR [17934](https://github.com/python/mypy/pull/17934)) + * Replace deprecated `_PyDict_GetItemStringWithError` (Marc Mueller, PR [17930](https://github.com/python/mypy/pull/17930)) + * Fix wheel build for cp313-win (Marc Mueller, PR [17941](https://github.com/python/mypy/pull/17941)) + * Use public PyGen_GetCode instead of vendored implementation (Marc Mueller, PR [17931](https://github.com/python/mypy/pull/17931)) + * Optimize calls to final classes (jairov4, PR [17886](https://github.com/python/mypy/pull/17886)) + * Support ellipsis (`...`) expressions in class bodies (Newbyte, PR [17923](https://github.com/python/mypy/pull/17923)) + * Sync `pythoncapi_compat.h` (Marc Mueller, PR [17929](https://github.com/python/mypy/pull/17929)) + * Add `runtests.py mypyc-fast` for running fast mypyc tests (Jukka Lehtosalo, PR [17906](https://github.com/python/mypy/pull/17906)) + +### Stubgen Improvements + + * Do not include mypy generated symbols (Ali Hamdan, PR [18137](https://github.com/python/mypy/pull/18137)) + * Fix `FunctionContext.fullname` for nested classes (Chad Dombrova, PR [17963](https://github.com/python/mypy/pull/17963)) + * Add flagfile support (Ruslan Sayfutdinov, PR [18061](https://github.com/python/mypy/pull/18061)) + * Add support for PEP 695 and PEP 696 syntax (Ali Hamdan, PR [18054](https://github.com/python/mypy/pull/18054)) + +### Stubtest Improvements + + * Allow the use of `--show-traceback` and `--pdb` with stubtest (Stephen Morton, PR [18037](https://github.com/python/mypy/pull/18037)) + * Verify `__all__` exists in stub (Sebastian Rittau, PR [18005](https://github.com/python/mypy/pull/18005)) + * Stop telling people to use double underscores (Jelle Zijlstra, PR [17897](https://github.com/python/mypy/pull/17897)) + +### Documentation Updates + + * Update config file documentation (sobolevn, PR [18103](https://github.com/python/mypy/pull/18103)) + * Improve contributor documentation for Windows (ag-tafe, PR [18097](https://github.com/python/mypy/pull/18097)) + * Correct note about `--disallow-any-generics` flag in documentation (Abel Sen, PR [18055](https://github.com/python/mypy/pull/18055)) + * Further caution against `--follow-imports=skip` (Shantanu, PR [18048](https://github.com/python/mypy/pull/18048)) + * Fix the edit page buttton link in documentation (Kanishk Pachauri, PR [17933](https://github.com/python/mypy/pull/17933)) + +### Other Notables Fixes and Improvements + + * Show `Protocol` `__call__` for arguments with incompatible types (MechanicalConstruct, PR [18214](https://github.com/python/mypy/pull/18214)) + * Make join and meet symmetric with `strict_optional` (MechanicalConstruct, PR [18227](https://github.com/python/mypy/pull/18227)) + * Preserve block unreachablility when checking function definitions with constrained TypeVars (Brian Schubert, PR [18217](https://github.com/python/mypy/pull/18217)) + * Do not include non-init fields in the synthesized `__replace__` method for dataclasses (Victorien, PR [18221](https://github.com/python/mypy/pull/18221)) + * Disallow `TypeVar` constraints parameterized by type variables (Brian Schubert, PR [18186](https://github.com/python/mypy/pull/18186)) + * Always complain about invalid varargs and varkwargs (Shantanu, PR [18207](https://github.com/python/mypy/pull/18207)) + * Set default strict_optional state to True (Shantanu, PR [18198](https://github.com/python/mypy/pull/18198)) + * Preserve type variable default None in type alias (Sukhorosov Aleksey, PR [18197](https://github.com/python/mypy/pull/18197)) + * Add checks for invalid usage of continue/break/return in `except*` block (coldwolverine, PR [18132](https://github.com/python/mypy/pull/18132)) + * Do not consider bare TypeVar not overlapping with None for reachability analysis (Stanislav Terliakov, PR [18138](https://github.com/python/mypy/pull/18138)) + * Special case `types.DynamicClassAttribute` as property-like (Stephen Morton, PR [18150](https://github.com/python/mypy/pull/18150)) + * Disallow bare `ParamSpec` in type aliases (Brian Schubert, PR [18174](https://github.com/python/mypy/pull/18174)) + * Move long_description metadata to pyproject.toml (Marc Mueller, PR [18172](https://github.com/python/mypy/pull/18172)) + * Support `==`-based narrowing of Optional (Christoph Tyralla, PR [18163](https://github.com/python/mypy/pull/18163)) + * Allow TypedDict assignment of Required item to NotRequired ReadOnly item (Brian Schubert, PR [18164](https://github.com/python/mypy/pull/18164)) + * Allow nesting of Annotated with TypedDict special forms inside TypedDicts (Brian Schubert, PR [18165](https://github.com/python/mypy/pull/18165)) + * Infer generic type arguments for slice expressions (Brian Schubert, PR [18160](https://github.com/python/mypy/pull/18160)) + * Fix checking of match sequence pattern against bounded type variables (Brian Schubert, PR [18091](https://github.com/python/mypy/pull/18091)) + * Fix incorrect truthyness for Enum types and literals (David Salvisberg, PR [17337](https://github.com/python/mypy/pull/17337)) + * Move static project metadata to pyproject.toml (Marc Mueller, PR [18146](https://github.com/python/mypy/pull/18146)) + * Fallback to stdlib json if integer exceeds 64-bit range (q0w, PR [18148](https://github.com/python/mypy/pull/18148)) + * Fix 'or' pattern structural matching exhaustiveness (yihong, PR [18119](https://github.com/python/mypy/pull/18119)) + * Fix type inference of positional parameter in class pattern involving builtin subtype (Brian Schubert, PR [18141](https://github.com/python/mypy/pull/18141)) + * Fix `[override]` error with no line number when argument node has no line number (Brian Schubert, PR [18122](https://github.com/python/mypy/pull/18122)) + * Fix some dmypy crashes (Ivan Levkivskyi, PR [18098](https://github.com/python/mypy/pull/18098)) + * Fix subtyping between instance type and overloaded (Shantanu, PR [18102](https://github.com/python/mypy/pull/18102)) + * Clean up new_semantic_analyzer config (Shantanu, PR [18071](https://github.com/python/mypy/pull/18071)) + * Issue warning for enum with no members in stub (Shantanu, PR [18068](https://github.com/python/mypy/pull/18068)) + * Fix enum attributes are not members (Terence Honles, PR [17207](https://github.com/python/mypy/pull/17207)) + * Fix crash when checking slice expression with step 0 in tuple index (Brian Schubert, PR [18063](https://github.com/python/mypy/pull/18063)) + * Allow union-with-callable attributes to be overridden by methods (Brian Schubert, PR [18018](https://github.com/python/mypy/pull/18018)) + * Emit `[mutable-override]` for covariant override of attribute with method (Brian Schubert, PR [18058](https://github.com/python/mypy/pull/18058)) + * Support ParamSpec mapping with `functools.partial` (Stanislav Terliakov, PR [17355](https://github.com/python/mypy/pull/17355)) + * Fix approved stub ignore, remove normpath (Shantanu, PR [18045](https://github.com/python/mypy/pull/18045)) + * Make `disallow-any-unimported` flag invertible (Séamus Ó Ceanainn, PR [18030](https://github.com/python/mypy/pull/18030)) + * Filter to possible package paths before trying to resolve a module (falsedrow, PR [18038](https://github.com/python/mypy/pull/18038)) + * Fix overlap check for ParamSpec types (Jukka Lehtosalo, PR [18040](https://github.com/python/mypy/pull/18040)) + * Do not prioritize ParamSpec signatures during overload resolution (Stanislav Terliakov, PR [18033](https://github.com/python/mypy/pull/18033)) + * Fix ternary union for literals (Ivan Levkivskyi, PR [18023](https://github.com/python/mypy/pull/18023)) + * Fix compatibility checks for conditional function definitions using decorators (Brian Schubert, PR [18020](https://github.com/python/mypy/pull/18020)) + * TypeGuard should be bool not Any when matching TypeVar (Evgeniy Slobodkin, PR [17145](https://github.com/python/mypy/pull/17145)) + * Fix convert-cache tool (Shantanu, PR [17974](https://github.com/python/mypy/pull/17974)) + * Fix generator comprehension with mypyc (Shantanu, PR [17969](https://github.com/python/mypy/pull/17969)) + * Fix crash issue when using shadowfile with pretty (Max Chang, PR [17894](https://github.com/python/mypy/pull/17894)) + * Fix multiple nested classes with new generics syntax (Max Chang, PR [17820](https://github.com/python/mypy/pull/17820)) + * Better error for `mypy -p package` without py.typed (Joe Gordon, PR [17908](https://github.com/python/mypy/pull/17908)) + * Emit error for `raise NotImplemented` (Brian Schubert, PR [17890](https://github.com/python/mypy/pull/17890)) + * Add `is_lvalue` attribute to AttributeContext (Brian Schubert, PR [17881](https://github.com/python/mypy/pull/17881)) + +### Acknowledgements + +Thanks to all mypy contributors who contributed to this release: + +- aatle +- Abel Sen +- Advait Dixit +- ag-tafe +- Alex Waygood +- Ali Hamdan +- Brian Schubert +- Carlton Gibson +- Chad Dombrova +- Chelsea Durazo +- chiri +- Christoph Tyralla +- coldwolverine +- David Salvisberg +- Ekin Dursun +- Evgeniy Slobodkin +- falsedrow +- Gaurav Giri +- Ihor +- Ivan Levkivskyi +- jairov4 +- Jannick Kremer +- Jared Hance +- Jelle Zijlstra +- jianghuyiyuan +- Joe Gordon +- John Doknjas +- Jukka Lehtosalo +- Kanishk Pachauri +- Marc Mueller +- Max Chang +- MechanicalConstruct +- Newbyte +- q0w +- Ruslan Sayfutdinov +- Sebastian Rittau +- Shantanu +- sobolevn +- Stanislav Terliakov +- Stephen Morton +- Sukhorosov Aleksey +- Séamus Ó Ceanainn +- Terence Honles +- Valentin Stanciu +- vasiliy +- Victorien +- yihong + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + + +## Mypy 1.13 + +We’ve just uploaded mypy 1.13 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). +Mypy is a static type checker for Python. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +Note that unlike typical releases, Mypy 1.13 does not have any changes to type checking semantics +from 1.12.1. + +### Improved Performance + +Mypy 1.13 contains several performance improvements. Users can expect mypy to be 5-20% faster. +In environments with long search paths (such as environments using many editable installs), mypy +can be significantly faster, e.g. 2.2x faster in the use case targeted by these improvements. + +Mypy 1.13 allows use of the `orjson` library for handling the cache instead of the stdlib `json`, +for improved performance. You can ensure the presence of `orjson` using the `faster-cache` extra: + + python3 -m pip install -U mypy[faster-cache] + +Mypy may depend on `orjson` by default in the future. + +These improvements were contributed by Shantanu. + +List of changes: +* Significantly speed up file handling error paths (Shantanu, PR [17920](https://github.com/python/mypy/pull/17920)) +* Use fast path in modulefinder more often (Shantanu, PR [17950](https://github.com/python/mypy/pull/17950)) +* Let mypyc optimise os.path.join (Shantanu, PR [17949](https://github.com/python/mypy/pull/17949)) +* Make is_sub_path faster (Shantanu, PR [17962](https://github.com/python/mypy/pull/17962)) +* Speed up stubs suggestions (Shantanu, PR [17965](https://github.com/python/mypy/pull/17965)) +* Use sha1 for hashing (Shantanu, PR [17953](https://github.com/python/mypy/pull/17953)) +* Use orjson instead of json, when available (Shantanu, PR [17955](https://github.com/python/mypy/pull/17955)) +* Add faster-cache extra, test in CI (Shantanu, PR [17978](https://github.com/python/mypy/pull/17978)) + +### Acknowledgements +Thanks to all mypy contributors who contributed to this release: + +- Shantanu Jain +- Jukka Lehtosalo + +## Mypy 1.12 + +We’ve just uploaded mypy 1.12 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type +checker for Python. This release includes new features, performance improvements and bug fixes. +You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +### Support Python 3.12 Syntax for Generics (PEP 695) + +Support for the new type parameter syntax introduced in Python 3.12 is now enabled by default, +documented, and no longer experimental. It was available through a feature flag in +mypy 1.11 as an experimental feature. + +This example demonstrates the new syntax: + +```python +# Generic function +def f[T](x: T) -> T: ... + +reveal_type(f(1)) # Revealed type is 'int' + +# Generic class +class C[T]: + def __init__(self, x: T) -> None: + self.x = x + +c = C('a') +reveal_type(c.x) # Revealed type is 'str' + +# Type alias +type A[T] = C[list[T]] +``` + +For more information, refer to the [documentation](https://mypy.readthedocs.io/en/latest/generics.html). + +These improvements are included: + + * Document Python 3.12 type parameter syntax (Jukka Lehtosalo, PR [17816](https://github.com/python/mypy/pull/17816)) + * Further documentation updates (Jukka Lehtosalo, PR [17826](https://github.com/python/mypy/pull/17826)) + * Allow Self return types with contravariance (Jukka Lehtosalo, PR [17786](https://github.com/python/mypy/pull/17786)) + * Enable new type parameter syntax by default (Jukka Lehtosalo, PR [17798](https://github.com/python/mypy/pull/17798)) + * Generate error if new-style type alias used as base class (Jukka Lehtosalo, PR [17789](https://github.com/python/mypy/pull/17789)) + * Inherit variance if base class has explicit variance (Jukka Lehtosalo, PR [17787](https://github.com/python/mypy/pull/17787)) + * Fix crash on invalid type var reference (Jukka Lehtosalo, PR [17788](https://github.com/python/mypy/pull/17788)) + * Fix covariance of frozen dataclasses (Jukka Lehtosalo, PR [17783](https://github.com/python/mypy/pull/17783)) + * Allow covariance with attribute that has "`_`" name prefix (Jukka Lehtosalo, PR [17782](https://github.com/python/mypy/pull/17782)) + * Support `Annotated[...]` in new-style type aliases (Jukka Lehtosalo, PR [17777](https://github.com/python/mypy/pull/17777)) + * Fix nested generic classes (Jukka Lehtosalo, PR [17776](https://github.com/python/mypy/pull/17776)) + * Add detection and error reporting for the use of incorrect expressions within the scope of a type parameter and a type alias (Kirill Podoprigora, PR [17560](https://github.com/python/mypy/pull/17560)) + +### Basic Support for Python 3.13 + +This release adds partial support for Python 3.13 features and compiled binaries for +Python 3.13. Mypyc now also supports Python 3.13. + +In particular, these features are supported: + * Various new stdlib features and changes (through typeshed stub improvements) + * `typing.ReadOnly` (see below for more) + * `typing.TypeIs` (added in mypy 1.10, [PEP 742](https://peps.python.org/pep-0742/)) + * Type parameter defaults when using the legacy syntax ([PEP 696](https://peps.python.org/pep-0696/)) + +These features are not supported yet: + * `warnings.deprecated` ([PEP 702](https://peps.python.org/pep-0702/)) + * Type parameter defaults when using Python 3.12 type parameter syntax + +### Mypyc Support for Python 3.13 + +Mypyc now supports Python 3.13. This was contributed by Marc Mueller, with additional +fixes by Jukka Lehtosalo. Free threaded Python 3.13 builds are not supported yet. + +List of changes: + + * Add additional includes for Python 3.13 (Marc Mueller, PR [17506](https://github.com/python/mypy/pull/17506)) + * Add another include for Python 3.13 (Marc Mueller, PR [17509](https://github.com/python/mypy/pull/17509)) + * Fix ManagedDict functions for Python 3.13 (Marc Mueller, PR [17507](https://github.com/python/mypy/pull/17507)) + * Update mypyc test output for Python 3.13 (Marc Mueller, PR [17508](https://github.com/python/mypy/pull/17508)) + * Fix `PyUnicode` functions for Python 3.13 (Marc Mueller, PR [17504](https://github.com/python/mypy/pull/17504)) + * Fix `_PyObject_LookupAttrId` for Python 3.13 (Marc Mueller, PR [17505](https://github.com/python/mypy/pull/17505)) + * Fix `_PyList_Extend` for Python 3.13 (Marc Mueller, PR [17503](https://github.com/python/mypy/pull/17503)) + * Fix `gen_is_coroutine` for Python 3.13 (Marc Mueller, PR [17501](https://github.com/python/mypy/pull/17501)) + * Fix `_PyObject_FastCall` for Python 3.13 (Marc Mueller, PR [17502](https://github.com/python/mypy/pull/17502)) + * Avoid uses of `_PyObject_CallMethodOneArg` on 3.13 (Jukka Lehtosalo, PR [17526](https://github.com/python/mypy/pull/17526)) + * Don't rely on `_PyType_CalculateMetaclass` on 3.13 (Jukka Lehtosalo, PR [17525](https://github.com/python/mypy/pull/17525)) + * Don't use `_PyUnicode_FastCopyCharacters` on 3.13 (Jukka Lehtosalo, PR [17524](https://github.com/python/mypy/pull/17524)) + * Don't use `_PyUnicode_EQ` on 3.13, as it's no longer exported (Jukka Lehtosalo, PR [17523](https://github.com/python/mypy/pull/17523)) + +### Inferring Unions for Conditional Expressions + +Mypy now always tries to infer a union type for a conditional expression if left and right +operand types are different. This results in more precise inferred types and lets mypy detect +more issues. Example: + +```python +s = "foo" if cond() else 1 +# Type of "s" is now "str | int" (it used to be "object") +``` + +Notably, if one of the operands has type `Any`, the type of a conditional expression is +now ` | Any`. Previously the inferred type was just `Any`. The new type essentially +indicates that the value can be of type ``, and potentially of some (unknown) type. +Most operations performed on the result must also be valid for ``. +Example where this is relevant: + +```python +from typing import Any + +def func(a: Any, b: bool) -> None: + x = a if b else None + # Type of x is "Any | None" + print(x.y) # Error: None has no attribute "y" +``` + +This feature was contributed by Ivan Levkivskyi (PR [17427](https://github.com/python/mypy/pull/17427)). + +### ReadOnly Support for TypedDict (PEP 705) + +You can now use `typing.ReadOnly` to specity TypedDict items as +read-only ([PEP 705](https://peps.python.org/pep-0705/)): + +```python +from typing import TypedDict + +# Or "from typing ..." on Python 3.13 +from typing_extensions import ReadOnly + +class TD(TypedDict): + a: int + b: ReadOnly[int] + +d: TD = {"a": 1, "b": 2} +d["a"] = 3 # OK +d["b"] = 5 # Error: "b" is ReadOnly +``` + +This feature was contributed by Nikita Sobolev (PR [17644](https://github.com/python/mypy/pull/17644)). + +### Python 3.8 End of Life Approaching + +We are planning to drop support for Python 3.8 in the next mypy feature release or the +one after that. Python 3.8 reaches end of life in October 2024. + +### Planned Changes to Defaults + +We are planning to enable `--local-partial-types` by default in mypy 2.0. This will +often require at least minor code changes. This option is implicitly enabled by mypy +daemon, so this makes the behavior of daemon and non-daemon modes consistent. + +We recommend that mypy users start using local partial types soon (or to explicitly disable +them) to prepare for the change. + +This can also be configured in a mypy configuration file: + +``` +local_partial_types = True +``` + +For more information, refer to the +[documentation](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-local-partial-types). + +### Documentation Updates + +Mypy documentation now uses modern syntax variants and imports in many examples. Some +examples no longer work on Python 3.8, which is the earliest Python version that mypy supports. + +Notably, `Iterable` and other protocols/ABCs are imported from `collections.abc` instead of +`typing`: +```python +from collections.abc import Iterable, Callable +``` + +Examples also avoid the upper-case aliases to built-in types: `list[str]` is used instead +of `List[str]`. The `X | Y` union type syntax introduced in Python 3.10 is also now prevalent. + +List of documentation updates: + + * Document `--output=json` CLI option (Edgar Ramírez Mondragón, PR [17611](https://github.com/python/mypy/pull/17611)) + * Update various references to deprecated type aliases in docs (Jukka Lehtosalo, PR [17829](https://github.com/python/mypy/pull/17829)) + * Make "X | Y" union syntax more prominent in documentation (Jukka Lehtosalo, PR [17835](https://github.com/python/mypy/pull/17835)) + * Discuss upper bounds before self types in documentation (Jukka Lehtosalo, PR [17827](https://github.com/python/mypy/pull/17827)) + * Make changelog visible in mypy documentation (quinn-sasha, PR [17742](https://github.com/python/mypy/pull/17742)) + * List all incomplete features in `--enable-incomplete-feature` docs (sobolevn, PR [17633](https://github.com/python/mypy/pull/17633)) + * Remove the explicit setting of a pygments theme (Pradyun Gedam, PR [17571](https://github.com/python/mypy/pull/17571)) + * Document ReadOnly with TypedDict (Jukka Lehtosalo, PR [17905](https://github.com/python/mypy/pull/17905)) + * Document TypeIs (Chelsea Durazo, PR [17821](https://github.com/python/mypy/pull/17821)) + +### Experimental Inline TypedDict Syntax + +Mypy now supports a non-standard, experimental syntax for defining anonymous TypedDicts. +Example: + +```python +def func(n: str, y: int) -> {"name": str, "year": int}: + return {"name": n, "year": y} +``` + +The feature is disabled by default. Use `--enable-incomplete-feature=InlineTypedDict` to +enable it. *We might remove this feature in a future release.* + +This feature was contributed by Ivan Levkivskyi (PR [17457](https://github.com/python/mypy/pull/17457)). + +### Stubgen Improvements + + * Fix crash on literal class-level keywords (sobolevn, PR [17663](https://github.com/python/mypy/pull/17663)) + * Stubgen add `--version` (sobolevn, PR [17662](https://github.com/python/mypy/pull/17662)) + * Fix `stubgen --no-analysis/--parse-only` docs (sobolevn, PR [17632](https://github.com/python/mypy/pull/17632)) + * Include keyword only args when generating signatures in stubgenc (Eric Mark Martin, PR [17448](https://github.com/python/mypy/pull/17448)) + * Add support for detecting `Literal` types when extracting types from docstrings (Michael Carlstrom, PR [17441](https://github.com/python/mypy/pull/17441)) + * Use `Generator` type var defaults (Sebastian Rittau, PR [17670](https://github.com/python/mypy/pull/17670)) + +### Stubtest Improvements + * Add support for `cached_property` (Ali Hamdan, PR [17626](https://github.com/python/mypy/pull/17626)) + * Add `enable_incomplete_feature` validation to `stubtest` (sobolevn, PR [17635](https://github.com/python/mypy/pull/17635)) + * Fix error code handling in `stubtest` with `--mypy-config-file` (sobolevn, PR [17629](https://github.com/python/mypy/pull/17629)) + +### Other Notables Fixes and Improvements + + * Report error if using unsupported type parameter defaults (Jukka Lehtosalo, PR [17876](https://github.com/python/mypy/pull/17876)) + * Fix re-processing cross-reference in mypy daemon when node kind changes (Ivan Levkivskyi, PR [17883](https://github.com/python/mypy/pull/17883)) + * Don't use equality to narrow when value is IntEnum/StrEnum (Jukka Lehtosalo, PR [17866](https://github.com/python/mypy/pull/17866)) + * Don't consider None vs IntEnum comparison ambiguous (Jukka Lehtosalo, PR [17877](https://github.com/python/mypy/pull/17877)) + * Fix narrowing of IntEnum and StrEnum types (Jukka Lehtosalo, PR [17874](https://github.com/python/mypy/pull/17874)) + * Filter overload items based on self type during type inference (Jukka Lehtosalo, PR [17873](https://github.com/python/mypy/pull/17873)) + * Enable negative narrowing of union TypeVar upper bounds (Brian Schubert, PR [17850](https://github.com/python/mypy/pull/17850)) + * Fix issue with member expression formatting (Brian Schubert, PR [17848](https://github.com/python/mypy/pull/17848)) + * Avoid type size explosion when expanding types (Jukka Lehtosalo, PR [17842](https://github.com/python/mypy/pull/17842)) + * Fix negative narrowing of tuples in match statement (Brian Schubert, PR [17817](https://github.com/python/mypy/pull/17817)) + * Narrow falsey str/bytes/int to literal type (Brian Schubert, PR [17818](https://github.com/python/mypy/pull/17818)) + * Test against latest Python 3.13, make testing 3.14 easy (Shantanu, PR [17812](https://github.com/python/mypy/pull/17812)) + * Reject ParamSpec-typed callables calls with insufficient arguments (Stanislav Terliakov, PR [17323](https://github.com/python/mypy/pull/17323)) + * Fix crash when passing too many type arguments to generic base class accepting single ParamSpec (Brian Schubert, PR [17770](https://github.com/python/mypy/pull/17770)) + * Fix TypeVar upper bounds sometimes not being displayed in pretty callables (Brian Schubert, PR [17802](https://github.com/python/mypy/pull/17802)) + * Added error code for overlapping function signatures (Katrina Connors, PR [17597](https://github.com/python/mypy/pull/17597)) + * Check for `truthy-bool` in `not ...` unary expressions (sobolevn, PR [17773](https://github.com/python/mypy/pull/17773)) + * Add missing lines-covered and lines-valid attributes (Soubhik Kumar Mitra, PR [17738](https://github.com/python/mypy/pull/17738)) + * Fix another crash scenario with recursive tuple types (Ivan Levkivskyi, PR [17708](https://github.com/python/mypy/pull/17708)) + * Resolve TypeVar upper bounds in `functools.partial` (Shantanu, PR [17660](https://github.com/python/mypy/pull/17660)) + * Always reset binder when checking deferred nodes (Ivan Levkivskyi, PR [17643](https://github.com/python/mypy/pull/17643)) + * Fix crash on a callable attribute with single unpack (Ivan Levkivskyi, PR [17641](https://github.com/python/mypy/pull/17641)) + * Fix mismatched signature between checker plugin API and implementation (bzoracler, PR [17343](https://github.com/python/mypy/pull/17343)) + * Indexing a type also produces a GenericAlias (Shantanu, PR [17546](https://github.com/python/mypy/pull/17546)) + * Fix crash on self-type in callable protocol (Ivan Levkivskyi, PR [17499](https://github.com/python/mypy/pull/17499)) + * Fix crash on NamedTuple with method and error in function (Ivan Levkivskyi, PR [17498](https://github.com/python/mypy/pull/17498)) + * Add `__replace__` for dataclasses in 3.13 (Max Muoto, PR [17469](https://github.com/python/mypy/pull/17469)) + * Fix help message for `--no-namespace-packages` (Raphael Krupinski, PR [17472](https://github.com/python/mypy/pull/17472)) + * Fix typechecking for async generators (Danny Yang, PR [17452](https://github.com/python/mypy/pull/17452)) + * Fix strict optional handling in attrs plugin (Ivan Levkivskyi, PR [17451](https://github.com/python/mypy/pull/17451)) + * Allow mixing ParamSpec and TypeVarTuple in Generic (Ivan Levkivskyi, PR [17450](https://github.com/python/mypy/pull/17450)) + * Improvements to `functools.partial` of types (Shantanu, PR [17898](https://github.com/python/mypy/pull/17898)) + * Make ReadOnly TypedDict items covariant (Jukka Lehtosalo, PR [17904](https://github.com/python/mypy/pull/17904)) + * Fix union callees with `functools.partial` (Jukka Lehtosalo, PR [17903](https://github.com/python/mypy/pull/17903)) + * Improve handling of generic functions with `functools.partial` (Ivan Levkivskyi, PR [17925](https://github.com/python/mypy/pull/17925)) + +### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=91a58b07cdd807b1d965e04ba85af2adab8bf924+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + +### Mypy 1.12.1 + * Fix crash when showing partially analyzed type in error message (Ivan Levkivskyi, PR [17961](https://github.com/python/mypy/pull/17961)) + * Fix iteration over union (when self type is involved) (Shantanu, PR [17976](https://github.com/python/mypy/pull/17976)) + * Fix type object with type var default in union context (Jukka Lehtosalo, PR [17991](https://github.com/python/mypy/pull/17991)) + * Revert change to `os.path` stubs affecting use of `os.PathLike[Any]` (Shantanu, PR [17995](https://github.com/python/mypy/pull/17995)) + +### Acknowledgements +Thanks to all mypy contributors who contributed to this release: + +- Ali Hamdan +- Anders Kaseorg +- Bénédikt Tran +- Brian Schubert +- bzoracler +- Chelsea Durazo +- Danny Yang +- Edgar Ramírez Mondragón +- Eric Mark Martin +- InSync +- Ivan Levkivskyi +- Jordandev678 +- Katrina Connors +- Kirill Podoprigora +- Marc Mueller +- Max Muoto +- Max Murin +- Michael Carlstrom +- Michael I Chen +- Pradyun Gedam +- quinn-sasha +- Raphael Krupinski +- Sebastian Rittau +- Shantanu +- sobolevn +- Soubhik Kumar Mitra +- Stanislav Terliakov +- wyattscarpenter + +I’d also like to thank my employer, Dropbox, for supporting mypy development. + + +## Mypy 1.11 + +We’ve just uploaded mypy 1.11 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows: + + python3 -m pip install -U mypy + +You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). + +### Support Python 3.12 Syntax for Generics (PEP 695) + +Mypy now supports the new type parameter syntax introduced in Python 3.12 ([PEP 695](https://peps.python.org/pep-0695/)). +This feature is still experimental and must be enabled with the `--enable-incomplete-feature=NewGenericSyntax` flag, or with `enable_incomplete_feature = NewGenericSyntax` in the mypy configuration file. +We plan to enable this by default in the next mypy feature release. + +This example demonstrates the new syntax: + +```python +# Generic function +def f[T](x: T) -> T: ... + +reveal_type(f(1)) # Revealed type is 'int' + +# Generic class +class C[T]: + def __init__(self, x: T) -> None: + self.x = x + +c = C('a') +reveal_type(c.x) # Revealed type is 'str' + +# Type alias +type A[T] = C[list[T]] +``` + +This feature was contributed by Jukka Lehtosalo. + + +### Support for `functools.partial` + +Mypy now type checks uses of `functools.partial`. Previously mypy would accept arbitrary arguments. + +This example will now produce an error: + +```python +from functools import partial + +def f(a: int, b: str) -> None: ... + +g = partial(f, 1) + +# Argument has incompatible type "int"; expected "str" +g(11) +``` + +This feature was contributed by Shantanu (PR [16939](https://github.com/python/mypy/pull/16939)). + + +### Stricter Checks for Untyped Overrides + +Past mypy versions didn't check if untyped methods were compatible with overridden methods. This would result in false negatives. Now mypy performs these checks when using `--check-untyped-defs`. + +For example, this now generates an error if using `--check-untyped-defs`: + +```python +class Base: + def f(self, x: int = 0) -> None: ... + +class Derived(Base): + # Signature incompatible with "Base" + def f(self): ... +``` + +This feature was contributed by Steven Troxler (PR [17276](https://github.com/python/mypy/pull/17276)). + + +### Type Inference Improvements + +The new polymorphic inference algorithm introduced in mypy 1.5 is now used in more situations. This improves type inference involving generic higher-order functions, in particular. + +This feature was contributed by Ivan Levkivskyi (PR [17348](https://github.com/python/mypy/pull/17348)). + +Mypy now uses unions of tuple item types in certain contexts to enable more precise inferred types. Example: + +```python +for x in (1, 'x'): + # Previously inferred as 'object' + reveal_type(x) # Revealed type is 'int | str' +``` + +This was also contributed by Ivan Levkivskyi (PR [17408](https://github.com/python/mypy/pull/17408)). + + +### Improvements to Detection of Overlapping Overloads + +The details of how mypy checks if two `@overload` signatures are unsafely overlapping were overhauled. This both fixes some false positives, and allows mypy to detect additional unsafe signatures. + +This feature was contributed by Ivan Levkivskyi (PR [17392](https://github.com/python/mypy/pull/17392)). + + +### Better Support for Type Hints in Expressions + +Mypy now allows more expressions that evaluate to valid type annotations in all expression contexts. The inferred types of these expressions are also sometimes more precise. Previously they were often `object`. + +This example uses a union type that includes a callable type as an expression, and it no longer generates an error: + +```python +from typing import Callable + +print(Callable[[], int] | None) # No error +``` + +This feature was contributed by Jukka Lehtosalo (PR [17404](https://github.com/python/mypy/pull/17404)). + + +### Mypyc Improvements + +Mypyc now supports the new syntax for generics introduced in Python 3.12 (see above). Another notable improvement is signficantly faster basic operations on `int` values. + + * Support Python 3.12 syntax for generic functions and classes (Jukka Lehtosalo, PR [17357](https://github.com/python/mypy/pull/17357)) + * Support Python 3.12 type alias syntax (Jukka Lehtosalo, PR [17384](https://github.com/python/mypy/pull/17384)) + * Fix ParamSpec (Shantanu, PR [17309](https://github.com/python/mypy/pull/17309)) + * Inline fast paths of integer unboxing operations (Jukka Lehtosalo, PR [17266](https://github.com/python/mypy/pull/17266)) + * Inline tagged integer arithmetic and bitwise operations (Jukka Lehtosalo, PR [17265](https://github.com/python/mypy/pull/17265)) + * Allow specifying primitives as pure (Jukka Lehtosalo, PR [17263](https://github.com/python/mypy/pull/17263)) + + +### Changes to Stubtest + * Ignore `_ios_support` (Alex Waygood, PR [17270](https://github.com/python/mypy/pull/17270)) + * Improve support for Python 3.13 (Shantanu, PR [17261](https://github.com/python/mypy/pull/17261)) + + +### Changes to Stubgen + * Gracefully handle invalid `Optional` and recognize aliases to PEP 604 unions (Ali Hamdan, PR [17386](https://github.com/python/mypy/pull/17386)) + * Fix for Python 3.13 (Jelle Zijlstra, PR [17290](https://github.com/python/mypy/pull/17290)) + * Preserve enum value initialisers (Shantanu, PR [17125](https://github.com/python/mypy/pull/17125)) + + +### Miscellaneous New Features + * Add error format support and JSON output option via `--output json` (Tushar Sadhwani, PR [11396](https://github.com/python/mypy/pull/11396)) + * Support `enum.member` in Python 3.11+ (Nikita Sobolev, PR [17382](https://github.com/python/mypy/pull/17382)) + * Support `enum.nonmember` in Python 3.11+ (Nikita Sobolev, PR [17376](https://github.com/python/mypy/pull/17376)) + * Support `namedtuple.__replace__` in Python 3.13 (Shantanu, PR [17259](https://github.com/python/mypy/pull/17259)) + * Support `rename=True` in collections.namedtuple (Jelle Zijlstra, PR [17247](https://github.com/python/mypy/pull/17247)) + * Add support for `__spec__` (Shantanu, PR [14739](https://github.com/python/mypy/pull/14739)) + + +### Changes to Error Reporting + * Mention `--enable-incomplete-feature=NewGenericSyntax` in messages (Shantanu, PR [17462](https://github.com/python/mypy/pull/17462)) + * Do not report plugin-generated methods with `explicit-override` (sobolevn, PR [17433](https://github.com/python/mypy/pull/17433)) + * Use and display namespaces for function type variables (Ivan Levkivskyi, PR [17311](https://github.com/python/mypy/pull/17311)) + * Fix false positive for Final local scope variable in Protocol (GiorgosPapoutsakis, PR [17308](https://github.com/python/mypy/pull/17308)) + * Use Never in more messages, use ambiguous in join (Shantanu, PR [17304](https://github.com/python/mypy/pull/17304)) + * Log full path to config file in verbose output (dexterkennedy, PR [17180](https://github.com/python/mypy/pull/17180)) + * Added `[prop-decorator]` code for unsupported property decorators (#14461) (Christopher Barber, PR [16571](https://github.com/python/mypy/pull/16571)) + * Suppress second error message with `:=` and `[truthy-bool]` (Nikita Sobolev, PR [15941](https://github.com/python/mypy/pull/15941)) + * Generate error for assignment of functional Enum to variable of different name (Shantanu, PR [16805](https://github.com/python/mypy/pull/16805)) + * Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) + + +### Fixes for Crashes + * Fix daemon crash on invalid type in TypedDict (Ivan Levkivskyi, PR [17495](https://github.com/python/mypy/pull/17495)) + * Fix crash and bugs related to `partial()` (Ivan Levkivskyi, PR [17423](https://github.com/python/mypy/pull/17423)) + * Fix crash when overriding with unpacked TypedDict (Ivan Levkivskyi, PR [17359](https://github.com/python/mypy/pull/17359)) + * Fix crash on TypedDict unpacking for ParamSpec (Ivan Levkivskyi, PR [17358](https://github.com/python/mypy/pull/17358)) + * Fix crash involving recursive union of tuples (Ivan Levkivskyi, PR [17353](https://github.com/python/mypy/pull/17353)) + * Fix crash on invalid callable property override (Ivan Levkivskyi, PR [17352](https://github.com/python/mypy/pull/17352)) + * Fix crash on unpacking self in NamedTuple (Ivan Levkivskyi, PR [17351](https://github.com/python/mypy/pull/17351)) + * Fix crash on recursive alias with an optional type (Ivan Levkivskyi, PR [17350](https://github.com/python/mypy/pull/17350)) + * Fix crash on type comment inside generic definitions (Bénédikt Tran, PR [16849](https://github.com/python/mypy/pull/16849)) + + +### Changes to Documentation + * Use inline config in documentation for optional error codes (Shantanu, PR [17374](https://github.com/python/mypy/pull/17374)) + * Use lower-case generics in documentation (Seo Sanghyeon, PR [17176](https://github.com/python/mypy/pull/17176)) + * Add documentation for show-error-code-links (GiorgosPapoutsakis, PR [17144](https://github.com/python/mypy/pull/17144)) + * Update CONTRIBUTING.md to include commands for Windows (GiorgosPapoutsakis, PR [17142](https://github.com/python/mypy/pull/17142)) + + +### Other Notable Improvements and Fixes + * Fix ParamSpec inference against TypeVarTuple (Ivan Levkivskyi, PR [17431](https://github.com/python/mypy/pull/17431)) + * Fix explicit type for `partial` (Ivan Levkivskyi, PR [17424](https://github.com/python/mypy/pull/17424)) + * Always allow lambda calls (Ivan Levkivskyi, PR [17430](https://github.com/python/mypy/pull/17430)) + * Fix isinstance checks with PEP 604 unions containing None (Shantanu, PR [17415](https://github.com/python/mypy/pull/17415)) + * Fix self-referential upper bound in new-style type variables (Ivan Levkivskyi, PR [17407](https://github.com/python/mypy/pull/17407)) + * Consider overlap between instances and callables (Ivan Levkivskyi, PR [17389](https://github.com/python/mypy/pull/17389)) + * Allow new-style self-types in classmethods (Ivan Levkivskyi, PR [17381](https://github.com/python/mypy/pull/17381)) + * Fix isinstance with type aliases to PEP 604 unions (Shantanu, PR [17371](https://github.com/python/mypy/pull/17371)) + * Properly handle unpacks in overlap checks (Ivan Levkivskyi, PR [17356](https://github.com/python/mypy/pull/17356)) + * Fix type application for classes with generic constructors (Ivan Levkivskyi, PR [17354](https://github.com/python/mypy/pull/17354)) + * Update `typing_extensions` to >=4.6.0 to fix Python 3.12 error (Ben Brown, PR [17312](https://github.com/python/mypy/pull/17312)) + * Avoid "does not return" error in lambda (Shantanu, PR [17294](https://github.com/python/mypy/pull/17294)) + * Fix bug with descriptors in non-strict-optional mode (Max Murin, PR [17293](https://github.com/python/mypy/pull/17293)) + * Don’t leak unreachability from lambda body to surrounding scope (Anders Kaseorg, PR [17287](https://github.com/python/mypy/pull/17287)) + * Fix issues with non-ASCII characters on Windows (Alexander Leopold Shon, PR [17275](https://github.com/python/mypy/pull/17275)) + * Fix for type narrowing of negative integer literals (gilesgc, PR [17256](https://github.com/python/mypy/pull/17256)) + * Fix confusion between .py and .pyi files in mypy daemon (Valentin Stanciu, PR [17245](https://github.com/python/mypy/pull/17245)) + * Fix type of `tuple[X, Y]` expression (urnest, PR [17235](https://github.com/python/mypy/pull/17235)) + * Don't forget that a `TypedDict` was wrapped in `Unpack` after a `name-defined` error occurred (Christoph Tyralla, PR [17226](https://github.com/python/mypy/pull/17226)) + * Mark annotated argument as having an explicit, not inferred type (bzoracler, PR [17217](https://github.com/python/mypy/pull/17217)) + * Don't consider Enum private attributes as enum members (Ali Hamdan, PR [17182](https://github.com/python/mypy/pull/17182)) + * Fix Literal strings containing pipe characters (Jelle Zijlstra, PR [17148](https://github.com/python/mypy/pull/17148)) + + +### Typeshed Updates + +Please see [git log](https://github.com/python/typeshed/commits/main?after=6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. + +### Mypy 1.11.1 + * Fix `RawExpressionType.accept` crash with `--cache-fine-grained` (Anders Kaseorg, PR [17588](https://github.com/python/mypy/pull/17588)) + * Fix PEP 604 isinstance caching (Shantanu, PR [17563](https://github.com/python/mypy/pull/17563)) + * Fix `typing.TypeAliasType` being undefined on python < 3.12 (Nikita Sobolev, PR [17558](https://github.com/python/mypy/pull/17558)) + * Fix `types.GenericAlias` lookup crash (Shantanu, PR [17543](https://github.com/python/mypy/pull/17543)) + +### Mypy 1.11.2 + * Alternative fix for a union-like literal string (Ivan Levkivskyi, PR [17639](https://github.com/python/mypy/pull/17639)) + * Unwrap `TypedDict` item types before storing (Ivan Levkivskyi, PR [17640](https://github.com/python/mypy/pull/17640)) + +### Acknowledgements +Thanks to all mypy contributors who contributed to this release: + +- Alex Waygood +- Alexander Leopold Shon +- Ali Hamdan +- Anders Kaseorg +- Ben Brown +- Bénédikt Tran +- bzoracler +- Christoph Tyralla +- Christopher Barber +- dexterkennedy +- gilesgc +- GiorgosPapoutsakis +- Ivan Levkivskyi +- Jelle Zijlstra +- Jukka Lehtosalo +- Marc Mueller +- Matthieu Devlin +- Michael R. Crusoe +- Nikita Sobolev +- Seo Sanghyeon +- Shantanu +- sobolevn +- Steven Troxler +- Tadeu Manoel +- Tamir Duberstein +- Tushar Sadhwani +- urnest +- Valentin Stanciu + +I’d also like to thank my employer, Dropbox, for supporting mypy development. ## Mypy 1.10 @@ -12,7 +895,7 @@ We’ve just uploaded mypy 1.10 to the Python Package Index ([PyPI](https://pypi You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Support TypeIs (PEP 742) +### Support TypeIs (PEP 742) Mypy now supports `TypeIs` ([PEP 742](https://peps.python.org/pep-0742/)), which allows functions to narrow the type of a value, similar to `isinstance()`. Unlike `TypeGuard`, @@ -39,7 +922,7 @@ can be used on earlier Python versions by importing it from This feature was contributed by Jelle Zijlstra (PR [16898](https://github.com/python/mypy/pull/16898)). -#### Support TypeVar Defaults (PEP 696) +### Support TypeVar Defaults (PEP 696) [PEP 696](https://peps.python.org/pep-0696/) adds support for type parameter defaults. Example: @@ -66,7 +949,7 @@ can be used with earlier Python releases by importing `TypeVar` from This feature was contributed by Marc Mueller (PR [16878](https://github.com/python/mypy/pull/16878) and PR [16925](https://github.com/python/mypy/pull/16925)). -#### Support TypeAliasType (PEP 695) +### Support TypeAliasType (PEP 695) As part of the initial steps towards implementing [PEP 695](https://peps.python.org/pep-0695/), mypy now supports `TypeAliasType`. `TypeAliasType` provides a backport of the new `type` statement in Python 3.12. @@ -102,7 +985,7 @@ c: ListOrSet[str] = 'test' # error: Incompatible types in assignment (expressio This feature was contributed by Ali Hamdan (PR [16926](https://github.com/python/mypy/pull/16926), PR [17038](https://github.com/python/mypy/pull/17038) and PR [17053](https://github.com/python/mypy/pull/17053)) -#### Detect Additional Unsafe Uses of super() +### Detect Additional Unsafe Uses of super() Mypy will reject unsafe uses of `super()` more consistently, when the target has a trivial (empty) body. Example: @@ -118,13 +1001,13 @@ class Sub(Proto): This feature was contributed by Shantanu (PR [16756](https://github.com/python/mypy/pull/16756)). -#### Stubgen Improvements +### Stubgen Improvements - Preserve empty tuple annotation (Ali Hamdan, PR [16907](https://github.com/python/mypy/pull/16907)) - Add support for PEP 570 positional-only parameters (Ali Hamdan, PR [16904](https://github.com/python/mypy/pull/16904)) - Replace obsolete typing aliases with builtin containers (Ali Hamdan, PR [16780](https://github.com/python/mypy/pull/16780)) - Fix generated dataclass `__init__` signature (Ali Hamdan, PR [16906](https://github.com/python/mypy/pull/16906)) -#### Mypyc Improvements +### Mypyc Improvements - Provide an easier way to define IR-to-IR transforms (Jukka Lehtosalo, PR [16998](https://github.com/python/mypy/pull/16998)) - Implement lowering pass and add primitives for int (in)equality (Jukka Lehtosalo, PR [17027](https://github.com/python/mypy/pull/17027)) @@ -135,15 +1018,15 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m - Fix compilation of unreachable comprehensions (Richard Si, PR [15721](https://github.com/python/mypy/pull/15721)) - Don't crash on non-inlinable final local reads (Richard Si, PR [15719](https://github.com/python/mypy/pull/15719)) -#### Documentation Improvements +### Documentation Improvements - Import `TypedDict` from `typing` instead of `typing_extensions` (Riccardo Di Maio, PR [16958](https://github.com/python/mypy/pull/16958)) - Add missing `mutable-override` to section title (James Braza, PR [16886](https://github.com/python/mypy/pull/16886)) -#### Error Reporting Improvements +### Error Reporting Improvements - Use lower-case generics more consistently in error messages (Jukka Lehtosalo, PR [17035](https://github.com/python/mypy/pull/17035)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes - Fix incorrect inferred type when accessing descriptor on union type (Matthieu Devlin, PR [16604](https://github.com/python/mypy/pull/16604)) - Fix crash when expanding invalid `Unpack` in a `Callable` alias (Ali Hamdan, PR [17028](https://github.com/python/mypy/pull/17028)) - Fix false positive when string formatting with string enum (roberfi, PR [16555](https://github.com/python/mypy/pull/16555)) @@ -161,15 +1044,15 @@ This feature was contributed by Shantanu (PR [16756](https://github.com/python/m - Experimental: Support TypedDict within `type[...]` (Marc Mueller, PR [16963](https://github.com/python/mypy/pull/16963)) - Experimtental: Fix issue with TypedDict with optional keys in `type[...]` (Marc Mueller, PR [17068](https://github.com/python/mypy/pull/17068)) -#### Typeshed Updates +### Typeshed Updates -Please see [git log](https://github.com/python/typeshed/commits/main?after=7c8e82fe483a40ec4cb0a2505cfdb0f3e7cc81d9+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. +Please see [git log](https://github.com/python/typeshed/commits/main?after=6dda799d8ad1d89e0f8aad7ac41d2d34bd838ace+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. -#### Mypy 1.10.1 +### Mypy 1.10.1 - Fix error reporting on cached run after uninstallation of third party library (Shantanu, PR [17420](https://github.com/python/mypy/pull/17420)) -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: - Alex Waygood @@ -212,7 +1095,7 @@ We’ve just uploaded mypy 1.9 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Breaking Changes +### Breaking Changes Because the version of typeshed we use in mypy 1.9 doesn't support 3.7, neither does mypy 1.9. (Jared Hance, PR [16883](https://github.com/python/mypy/pull/16883)) @@ -234,7 +1117,7 @@ projects to use `--local-partial-types`, but it's not yet clear whether this is practical. The migration usually involves adding some explicit type annotations to module-level and class-level variables. -#### Basic Support for Type Parameter Defaults (PEP 696) +### Basic Support for Type Parameter Defaults (PEP 696) This release contains new experimental support for type parameter defaults ([PEP 696](https://peps.python.org/pep-0696)). Please try it @@ -264,7 +1147,7 @@ reveal_type(Context().bot) reveal_type(Context[MyBot]().bot) ``` -#### Type-checking Improvements +### Type-checking Improvements * Fix missing type store for overloads (Marc Mueller, PR [16803](https://github.com/python/mypy/pull/16803)) * Fix `'WriteToConn' object has no attribute 'flush'` (Charlie Denton, PR [16801](https://github.com/python/mypy/pull/16801)) * Improve TypeAlias error messages (Marc Mueller, PR [16831](https://github.com/python/mypy/pull/16831)) @@ -277,11 +1160,11 @@ reveal_type(Context[MyBot]().bot) * Add `alias` support to `field()` in `attrs` plugin (Nikita Sobolev, PR [16610](https://github.com/python/mypy/pull/16610)) * Improve attrs hashability detection (Tin Tvrtković, PR [16556](https://github.com/python/mypy/pull/16556)) -#### Performance Improvements +### Performance Improvements * Speed up finding function type variables (Jukka Lehtosalo, PR [16562](https://github.com/python/mypy/pull/16562)) -#### Documentation Updates +### Documentation Updates * Document supported values for `--enable-incomplete-feature` in "mypy --help" (Froger David, PR [16661](https://github.com/python/mypy/pull/16661)) * Update new type system discussion links (thomaswhaley, PR [16841](https://github.com/python/mypy/pull/16841)) @@ -291,7 +1174,7 @@ reveal_type(Context[MyBot]().bot) * Fix numbering error (Stefanie Molin, PR [16838](https://github.com/python/mypy/pull/16838)) * Various documentation improvements (Shantanu, PR [16836](https://github.com/python/mypy/pull/16836)) -#### Stubtest Improvements +### Stubtest Improvements * Ignore private function/method parameters when they are missing from the stub (private parameter names start with a single underscore and have a default) (PR [16507](https://github.com/python/mypy/pull/16507)) * Ignore a new protocol dunder (Alex Waygood, PR [16895](https://github.com/python/mypy/pull/16895)) * Private parameters can be omitted (Sebastian Rittau, PR [16507](https://github.com/python/mypy/pull/16507)) @@ -299,13 +1182,13 @@ reveal_type(Context[MyBot]().bot) * Adjust symbol table logic (Shantanu, PR [16823](https://github.com/python/mypy/pull/16823)) * Fix posisitional-only handling in overload resolution (Shantanu, PR [16750](https://github.com/python/mypy/pull/16750)) -#### Stubgen Improvements +### Stubgen Improvements * Fix crash on star unpack of TypeVarTuple (Ali Hamdan, PR [16869](https://github.com/python/mypy/pull/16869)) * Use PEP 604 unions everywhere (Ali Hamdan, PR [16519](https://github.com/python/mypy/pull/16519)) * Do not ignore property deleter (Ali Hamdan, PR [16781](https://github.com/python/mypy/pull/16781)) * Support type stub generation for `staticmethod` (WeilerMarcel, PR [14934](https://github.com/python/mypy/pull/14934)) -#### Acknowledgements +### Acknowledgements ​Thanks to all mypy contributors who contributed to this release: @@ -349,7 +1232,7 @@ We’ve just uploaded mypy 1.8 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Type-checking Improvements +### Type-checking Improvements * Do not intersect types in isinstance checks if at least one is final (Christoph Tyralla, PR [16330](https://github.com/python/mypy/pull/16330)) * Detect that `@final` class without `__bool__` cannot have falsey instances (Ilya Priven, PR [16566](https://github.com/python/mypy/pull/16566)) * Do not allow `TypedDict` classes with extra keywords (Nikita Sobolev, PR [16438](https://github.com/python/mypy/pull/16438)) @@ -359,44 +1242,44 @@ You can read the full documentation for this release on [Read the Docs](http://m * Allow type ignores of PEP 695 constructs (Shantanu, PR [16608](https://github.com/python/mypy/pull/16608)) * Enable `type_check_only` support for `TypedDict` and `NamedTuple` (Nikita Sobolev, PR [16469](https://github.com/python/mypy/pull/16469)) -#### Performance Improvements +### Performance Improvements * Add fast path to analyzing special form assignments (Jukka Lehtosalo, PR [16561](https://github.com/python/mypy/pull/16561)) -#### Improvements to Error Reporting +### Improvements to Error Reporting * Don't show documentation links for plugin error codes (Ivan Levkivskyi, PR [16383](https://github.com/python/mypy/pull/16383)) * Improve error messages for `super` checks and add more tests (Nikita Sobolev, PR [16393](https://github.com/python/mypy/pull/16393)) * Add error code for mutable covariant override (Ivan Levkivskyi, PR [16399](https://github.com/python/mypy/pull/16399)) -#### Stubgen Improvements +### Stubgen Improvements * Preserve simple defaults in function signatures (Ali Hamdan, PR [15355](https://github.com/python/mypy/pull/15355)) * Include `__all__` in output (Jelle Zijlstra, PR [16356](https://github.com/python/mypy/pull/16356)) * Fix stubgen regressions with pybind11 and mypy 1.7 (Chad Dombrova, PR [16504](https://github.com/python/mypy/pull/16504)) -#### Stubtest Improvements +### Stubtest Improvements * Improve handling of unrepresentable defaults (Jelle Zijlstra, PR [16433](https://github.com/python/mypy/pull/16433)) * Print more helpful errors if a function is missing from stub (Alex Waygood, PR [16517](https://github.com/python/mypy/pull/16517)) * Support `@type_check_only` decorator (Nikita Sobolev, PR [16422](https://github.com/python/mypy/pull/16422)) * Warn about missing `__del__` (Shantanu, PR [16456](https://github.com/python/mypy/pull/16456)) * Fix crashes with some uses of `final` and `deprecated` (Shantanu, PR [16457](https://github.com/python/mypy/pull/16457)) -#### Fixes to Crashes +### Fixes to Crashes * Fix crash with type alias to `Callable[[Unpack[Tuple[Any, ...]]], Any]` (Alex Waygood, PR [16541](https://github.com/python/mypy/pull/16541)) * Fix crash on TypeGuard in `__call__` (Ivan Levkivskyi, PR [16516](https://github.com/python/mypy/pull/16516)) * Fix crash on invalid enum in method (Ivan Levkivskyi, PR [16511](https://github.com/python/mypy/pull/16511)) * Fix crash on unimported Any in TypedDict (Ivan Levkivskyi, PR [16510](https://github.com/python/mypy/pull/16510)) -#### Documentation Updates +### Documentation Updates * Update soft-error-limit default value to -1 (Sveinung Gundersen, PR [16542](https://github.com/python/mypy/pull/16542)) * Support Sphinx 7.x (Michael R. Crusoe, PR [16460](https://github.com/python/mypy/pull/16460)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes * Allow mypy to output a junit file with per-file results (Matthew Wright, PR [16388](https://github.com/python/mypy/pull/16388)) -#### Typeshed Updates +### Typeshed Updates Please see [git log](https://github.com/python/typeshed/commits/main?after=4a854366e03dee700109f8e758a08b2457ea2f51+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. -#### Acknowledgements +### Acknowledgements ​Thanks to all mypy contributors who contributed to this release: @@ -430,7 +1313,7 @@ We’ve just uploaded mypy 1.7 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Using TypedDict for `**kwargs` Typing +### Using TypedDict for `**kwargs` Typing Mypy now has support for using `Unpack[...]` with a TypedDict type to annotate `**kwargs` arguments enabled by default. Example: @@ -460,7 +1343,7 @@ Refer to [PEP 692](https://peps.python.org/pep-0692/) for more information. Note This was contributed by Ivan Levkivskyi back in 2022 (PR [13471](https://github.com/python/mypy/pull/13471)). -#### TypeVarTuple Support Enabled (Experimental) +### TypeVarTuple Support Enabled (Experimental) Mypy now has support for variadic generics (TypeVarTuple) enabled by default, as an experimental feature. Refer to [PEP 646](https://peps.python.org/pep-0646/) for the details. @@ -482,7 +1365,7 @@ Changes included in this release: * Subtyping and inference of user-defined variadic types (Ivan Levkivskyi, PR [16076](https://github.com/python/mypy/pull/16076)) * Complete type analysis of variadic types (Ivan Levkivskyi, PR [15991](https://github.com/python/mypy/pull/15991)) -#### New Way of Installing Mypyc Dependencies +### New Way of Installing Mypyc Dependencies If you want to install package dependencies needed by mypyc (not just mypy), you should now install `mypy[mypyc]` instead of just `mypy`: @@ -494,13 +1377,13 @@ Mypy has many more users than mypyc, so always installing mypyc dependencies wou This change was contributed by Shantanu (PR [16229](https://github.com/python/mypy/pull/16229)). -#### New Rules for Re-exports +### New Rules for Re-exports Mypy no longer considers an import such as `import a.b as b` as an explicit re-export. The old behavior was arguably inconsistent and surprising. This may impact some stub packages, such as older versions of `types-six`. You can change the import to `from a import b as b`, if treating the import as a re-export was intentional. This change was contributed by Anders Kaseorg (PR [14086](https://github.com/python/mypy/pull/14086)). -#### Improved Type Inference +### Improved Type Inference The new type inference algorithm that was recently introduced to mypy (but was not enabled by default) is now enabled by default. It improves type inference of calls to generic callables where an argument is also a generic callable, in particular. You can use `--old-type-inference` to disable the new behavior. @@ -508,7 +1391,7 @@ The new algorithm can (rarely) produce different error messages, different error The new type inference algorithm was contributed by Ivan Levkivskyi. PR [16345](https://github.com/python/mypy/pull/16345) enabled it by default. -#### Narrowing Tuple Types Using len() +### Narrowing Tuple Types Using len() Mypy now can narrow tuple types using `len()` checks. Example: @@ -521,7 +1404,7 @@ def f(t: tuple[int, int] | tuple[int, int, int]) -> None: This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). -#### More Precise Tuple Lengths (Experimental) +### More Precise Tuple Lengths (Experimental) Mypy supports experimental, more precise checking of tuple type lengths through `--enable-incomplete-feature=PreciseTupleTypes`. Refer to the [documentation](https://mypy.readthedocs.io/en/latest/command_line.html#enabling-incomplete-experimental-features) for more information. @@ -529,13 +1412,13 @@ More generally, we are planning to use `--enable-incomplete-feature` to introduc This feature was contributed by Ivan Levkivskyi (PR [16237](https://github.com/python/mypy/pull/16237)). -#### Mypy Changelog +### Mypy Changelog We now maintain a [changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) in the mypy Git repository. It mirrors the contents of [mypy release blog posts](https://mypy-lang.blogspot.com/). We will continue to also publish release blog posts. In the future, release blog posts will be created based on the changelog near a release date. This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull/16280)). -#### Mypy Daemon Improvements +### Mypy Daemon Improvements * Fix daemon crash caused by deleted submodule (Jukka Lehtosalo, PR [16370](https://github.com/python/mypy/pull/16370)) * Fix file reloading in dmypy with --export-types (Ivan Levkivskyi, PR [16359](https://github.com/python/mypy/pull/16359)) @@ -547,7 +1430,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Stream dmypy output instead of dumping everything at the end (Valentin Stanciu, PR [16252](https://github.com/python/mypy/pull/16252)) * Make sure all dmypy errors are shown (Valentin Stanciu, PR [16250](https://github.com/python/mypy/pull/16250)) -#### Mypyc Improvements +### Mypyc Improvements * Generate error on duplicate function definitions (Jukka Lehtosalo, PR [16309](https://github.com/python/mypy/pull/16309)) * Don't crash on unreachable statements (Jukka Lehtosalo, PR [16311](https://github.com/python/mypy/pull/16311)) @@ -555,7 +1438,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Fix direct `__dict__` access on inner functions in new Python (Shantanu, PR [16084](https://github.com/python/mypy/pull/16084)) * Make tuple packing and unpacking more efficient (Jukka Lehtosalo, PR [16022](https://github.com/python/mypy/pull/16022)) -#### Improvements to Error Reporting +### Improvements to Error Reporting * Update starred expression error message to match CPython (Cibin Mathew, PR [16304](https://github.com/python/mypy/pull/16304)) * Fix error code of "Maybe you forgot to use await" note (Jelle Zijlstra, PR [16203](https://github.com/python/mypy/pull/16203)) @@ -565,14 +1448,14 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Add hint for AsyncIterator incompatible return type (Ilya Priven, PR [15883](https://github.com/python/mypy/pull/15883)) * Don't suggest stubs packages where the runtime package now ships with types (Alex Waygood, PR [16226](https://github.com/python/mypy/pull/16226)) -#### Performance Improvements +### Performance Improvements * Speed up type argument checking (Jukka Lehtosalo, PR [16353](https://github.com/python/mypy/pull/16353)) * Add fast path for checking self types (Jukka Lehtosalo, PR [16352](https://github.com/python/mypy/pull/16352)) * Cache information about whether file is typeshed file (Jukka Lehtosalo, PR [16351](https://github.com/python/mypy/pull/16351)) * Skip expensive `repr()` in logging call when not needed (Jukka Lehtosalo, PR [16350](https://github.com/python/mypy/pull/16350)) -#### Attrs and Dataclass Improvements +### Attrs and Dataclass Improvements * `dataclass.replace`: Allow transformed classes (Ilya Priven, PR [15915](https://github.com/python/mypy/pull/15915)) * `dataclass.replace`: Fall through to typeshed signature (Ilya Priven, PR [15962](https://github.com/python/mypy/pull/15962)) @@ -581,7 +1464,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * `attrs`, `dataclasses`: Don't enforce slots when base class doesn't (Ilya Priven, PR [15976](https://github.com/python/mypy/pull/15976)) * Fix crash on dataclass field / property collision (Nikita Sobolev, PR [16147](https://github.com/python/mypy/pull/16147)) -#### Stubgen Improvements +### Stubgen Improvements * Write stubs with utf-8 encoding (Jørgen Lind, PR [16329](https://github.com/python/mypy/pull/16329)) * Fix missing property setter in semantic analysis mode (Ali Hamdan, PR [16303](https://github.com/python/mypy/pull/16303)) @@ -589,7 +1472,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Multiple fixes to the generated imports (Ali Hamdan, PR [15624](https://github.com/python/mypy/pull/15624)) * Generate valid dataclass stubs (Ali Hamdan, PR [15625](https://github.com/python/mypy/pull/15625)) -#### Fixes to Crashes +### Fixes to Crashes * Fix incremental mode crash on TypedDict in method (Ivan Levkivskyi, PR [16364](https://github.com/python/mypy/pull/16364)) * Fix crash on star unpack in TypedDict (Ivan Levkivskyi, PR [16116](https://github.com/python/mypy/pull/16116)) @@ -598,7 +1481,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Fix crash when parsing error code config with typo (Shantanu, PR [16005](https://github.com/python/mypy/pull/16005)) * Fix `__post_init__()` internal error (Ilya Priven, PR [16080](https://github.com/python/mypy/pull/16080)) -#### Documentation Updates +### Documentation Updates * Make it easier to copy commands from README (Hamir Mahal, PR [16133](https://github.com/python/mypy/pull/16133)) * Document and rename `[overload-overlap]` error code (Shantanu, PR [16074](https://github.com/python/mypy/pull/16074)) @@ -606,7 +1489,7 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Document `force_union_syntax` and `force_uppercase_builtins` (Nikita Sobolev, PR [16048](https://github.com/python/mypy/pull/16048)) * Document we're not tracking relationships between symbols (Ilya Priven, PR [16018](https://github.com/python/mypy/pull/16018)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes * Propagate narrowed types to lambda expressions (Ivan Levkivskyi, PR [16407](https://github.com/python/mypy/pull/16407)) * Avoid importing from `setuptools._distutils` (Shantanu, PR [16348](https://github.com/python/mypy/pull/16348)) @@ -640,11 +1523,11 @@ This was contributed by Shantanu (PR [16280](https://github.com/python/mypy/pull * Make iterable logic more consistent (Shantanu, PR [16006](https://github.com/python/mypy/pull/16006)) * Fix inference for properties with `__call__` (Shantanu, PR [15926](https://github.com/python/mypy/pull/15926)) -#### Typeshed Updates +### Typeshed Updates Please see [git log](https://github.com/python/typeshed/commits/main?after=4a854366e03dee700109f8e758a08b2457ea2f51+0&branch=main&path=stdlib) for full list of standard library typeshed stub changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -690,7 +1573,7 @@ We’ve just uploaded mypy 1.6 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Introduce Error Subcodes for Import Errors +### Introduce Error Subcodes for Import Errors Mypy now uses the error code import-untyped if an import targets an installed library that doesn’t support static type checking, and no stub files are available. Other invalid imports produce the import-not-found error code. They both are subcodes of the import error code, which was previously used for both kinds of import-related errors. @@ -700,17 +1583,17 @@ If you use \--warn-unused-ignore or \--strict, mypy will complain if you use \# This feature was contributed by Shantanu (PR [15840](https://github.com/python/mypy/pull/15840), PR [14740](https://github.com/python/mypy/pull/14740)). -#### Remove Support for Targeting Python 3.6 and Earlier +### Remove Support for Targeting Python 3.6 and Earlier Running mypy with \--python-version 3.6, for example, is no longer supported. Python 3.6 hasn’t been properly supported by mypy for some time now, and this makes it explicit. This was contributed by Nikita Sobolev (PR [15668](https://github.com/python/mypy/pull/15668)). -#### Selective Filtering of \--disallow-untyped-calls Targets +### Selective Filtering of \--disallow-untyped-calls Targets Using \--disallow-untyped-calls could be annoying when using libraries with missing type information, as mypy would generate many errors about code that uses the library. Now you can use \--untyped-calls-exclude=acme, for example, to disable these errors about calls targeting functions defined in the acme package. Refer to the [documentation](https://mypy.readthedocs.io/en/latest/command_line.html#cmdoption-mypy-untyped-calls-exclude) for more information. This feature was contributed by Ivan Levkivskyi (PR [15845](https://github.com/python/mypy/pull/15845)). -#### Improved Type Inference between Callable Types +### Improved Type Inference between Callable Types Mypy now does a better job inferring type variables inside arguments of callable types. For example, this code fragment now type checks correctly: @@ -723,7 +1606,7 @@ reveal_type(f(g)) # Callable[[str, int, int], None] This was contributed by Ivan Levkivskyi (PR [15910](https://github.com/python/mypy/pull/15910)). -#### Don’t Consider None and TypeVar to Overlap in Overloads +### Don’t Consider None and TypeVar to Overlap in Overloads Mypy now doesn’t consider an overload item with an argument type None to overlap with a type variable: @@ -739,7 +1622,7 @@ Previously mypy would generate an error about the definition of f above. This is This feature was contributed by Ivan Levkivskyi (PR [15846](https://github.com/python/mypy/pull/15846)). -#### Improvements to \--new-type-inference +### Improvements to \--new-type-inference The experimental new type inference algorithm (polymorphic inference) introduced as an opt-in feature in mypy 1.5 has several improvements: @@ -751,7 +1634,7 @@ The experimental new type inference algorithm (polymorphic inference) introduced **Note:** We are planning to enable \--new-type-inference by default in mypy 1.7. Please try this out and let us know if you encounter any issues. -#### ParamSpec Improvements +### ParamSpec Improvements * Support self-types containing ParamSpec (Ivan Levkivskyi, PR [15903](https://github.com/python/mypy/pull/15903)) * Allow “…” in Concatenate, and clean up ParamSpec literals (Ivan Levkivskyi, PR [15905](https://github.com/python/mypy/pull/15905)) @@ -760,12 +1643,12 @@ The experimental new type inference algorithm (polymorphic inference) introduced * Fix crash on invalid type variable with ParamSpec (Ivan Levkivskyi, PR [15953](https://github.com/python/mypy/pull/15953)) * Fix subtyping between ParamSpecs (Ivan Levkivskyi, PR [15892](https://github.com/python/mypy/pull/15892)) -#### Stubgen Improvements +### Stubgen Improvements * Add option to include docstrings with stubgen (chylek, PR [13284](https://github.com/python/mypy/pull/13284)) * Add required ... initializer to NamedTuple fields with default values (Nikita Sobolev, PR [15680](https://github.com/python/mypy/pull/15680)) -#### Stubtest Improvements +### Stubtest Improvements * Fix \_\_mypy-replace false positives (Alex Waygood, PR [15689](https://github.com/python/mypy/pull/15689)) * Fix edge case for bytes enum subclasses (Alex Waygood, PR [15943](https://github.com/python/mypy/pull/15943)) @@ -773,14 +1656,14 @@ The experimental new type inference algorithm (polymorphic inference) introduced * Fixes to new check for missing stdlib modules (Alex Waygood, PR [15960](https://github.com/python/mypy/pull/15960)) * Fix stubtest enum.Flag edge case (Alex Waygood, PR [15933](https://github.com/python/mypy/pull/15933)) -#### Documentation Improvements +### Documentation Improvements * Do not advertise to create your own assert\_never helper (Nikita Sobolev, PR [15947](https://github.com/python/mypy/pull/15947)) * Fix all the missing references found within the docs (Albert Tugushev, PR [15875](https://github.com/python/mypy/pull/15875)) * Document await-not-async error code (Shantanu, PR [15858](https://github.com/python/mypy/pull/15858)) * Improve documentation of disabling error codes (Shantanu, PR [15841](https://github.com/python/mypy/pull/15841)) -#### Other Notable Changes and Fixes +### Other Notable Changes and Fixes * Make unsupported PEP 695 features (introduced in Python 3.12) give a reasonable error message (Shantanu, PR [16013](https://github.com/python/mypy/pull/16013)) * Remove the \--py2 command-line argument (Marc Mueller, PR [15670](https://github.com/python/mypy/pull/15670)) @@ -803,11 +1686,11 @@ The experimental new type inference algorithm (polymorphic inference) introduced * Add tox.ini to mypy sdist (Marcel Telka, PR [15853](https://github.com/python/mypy/pull/15853)) * Fix mypyc regression with pretty (Shantanu, PR [16124](https://github.com/python/mypy/pull/16124)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=6a8d653a671925b0a3af61729ff8cf3f90c9c662+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to Max Murin, who did most of the release manager work for this release (I just did the final steps). @@ -846,11 +1729,11 @@ We’ve just uploaded mypy 1.5 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Drop Support for Python 3.7 +### Drop Support for Python 3.7 Mypy no longer supports running with Python 3.7, which has reached end-of-life. This was contributed by Shantanu (PR [15566](https://github.com/python/mypy/pull/15566)). -#### Optional Check to Require Explicit @override +### Optional Check to Require Explicit @override If you enable the explicit-override error code, mypy will generate an error if a method override doesn’t use the @typing.override decorator (as discussed in [PEP 698](https://peps.python.org/pep-0698/#strict-enforcement-per-project)). This way mypy will detect accidentally introduced overrides. Example: @@ -880,7 +1763,7 @@ The override decorator will be available in typing in Python 3.12, but you can a This feature was contributed by Marc Mueller(PR [15512](https://github.com/python/mypy/pull/15512)). -#### More Flexible TypedDict Creation and Update +### More Flexible TypedDict Creation and Update Mypy was previously overly strict when type checking TypedDict creation and update operations. Though these checks were often technically correct, they sometimes triggered for apparently valid code. These checks have now been relaxed by default. You can enable stricter checking by using the new \--extra-checks flag. @@ -908,11 +1791,11 @@ a.update(b) # OK (previously an error) This feature was contributed by Ivan Levkivskyi (PR [15425](https://github.com/python/mypy/pull/15425)). -#### Deprecated Flag: \--strict-concatenate +### Deprecated Flag: \--strict-concatenate The behavior of \--strict-concatenate is now included in the new \--extra-checks flag, and the old flag is deprecated. -#### Optionally Show Links to Error Code Documentation +### Optionally Show Links to Error Code Documentation If you use \--show-error-code-links, mypy will add documentation links to (many) reported errors. The links are not shown for error messages that are sufficiently obvious, and they are shown once per error code only. @@ -923,19 +1806,19 @@ a.py:1: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-var-annotated f ``` This was contributed by Ivan Levkivskyi (PR [15449](https://github.com/python/mypy/pull/15449)). -#### Consistently Avoid Type Checking Unreachable Code +### Consistently Avoid Type Checking Unreachable Code If a module top level has unreachable code, mypy won’t type check the unreachable statements. This is consistent with how functions behave. The behavior of \--warn-unreachable is also more consistent now. This was contributed by Ilya Priven (PR [15386](https://github.com/python/mypy/pull/15386)). -#### Experimental Improved Type Inference for Generic Functions +### Experimental Improved Type Inference for Generic Functions You can use \--new-type-inference to opt into an experimental new type inference algorithm. It fixes issues when calling a generic functions with an argument that is also a generic function, in particular. This current implementation is still incomplete, but we encourage trying it out and reporting bugs if you encounter regressions. We are planning to enable the new algorithm by default in a future mypy release. This feature was contributed by Ivan Levkivskyi (PR [15287](https://github.com/python/mypy/pull/15287)). -#### Partial Support for Python 3.12 +### Partial Support for Python 3.12 Mypy and mypyc now support running on recent Python 3.12 development versions. Not all new Python 3.12 features are supported, and we don’t ship compiled wheels for Python 3.12 yet. @@ -951,7 +1834,7 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * mypyc: Don't use \_PyErr\_ChainExceptions on 3.12, since it's deprecated (Jukka Lehtosalo, PR [15468](https://github.com/python/mypy/pull/15468)) * mypyc: Add Python 3.12 feature macro (Jukka Lehtosalo, PR [15465](https://github.com/python/mypy/pull/15465)) -#### Improvements to Dataclasses +### Improvements to Dataclasses * Improve signature of dataclasses.replace (Ilya Priven, PR [14849](https://github.com/python/mypy/pull/14849)) * Fix dataclass/protocol crash on joining types (Ilya Priven, PR [15629](https://github.com/python/mypy/pull/15629)) @@ -960,7 +1843,7 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * Add `__slots__` attribute to dataclasses (Nikita Sobolev, PR [15649](https://github.com/python/mypy/pull/15649)) * Support better \_\_post\_init\_\_ method signature for dataclasses (Nikita Sobolev, PR [15503](https://github.com/python/mypy/pull/15503)) -#### Mypyc Improvements +### Mypyc Improvements * Support unsigned 8-bit native integer type: mypy\_extensions.u8 (Jukka Lehtosalo, PR [15564](https://github.com/python/mypy/pull/15564)) * Support signed 16-bit native integer type: mypy\_extensions.i16 (Jukka Lehtosalo, PR [15464](https://github.com/python/mypy/pull/15464)) @@ -970,20 +1853,20 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * Use C99 compound literals for undefined tuple values (Jukka Lehtosalo, PR [15453](https://github.com/python/mypy/pull/15453)) * Don't explicitly assign NULL values in setup functions (Logan Hunt, PR [15379](https://github.com/python/mypy/pull/15379)) -#### Stubgen Improvements +### Stubgen Improvements * Teach stubgen to work with complex and unary expressions (Nikita Sobolev, PR [15661](https://github.com/python/mypy/pull/15661)) * Support ParamSpec and TypeVarTuple (Ali Hamdan, PR [15626](https://github.com/python/mypy/pull/15626)) * Fix crash on non-str docstring (Ali Hamdan, PR [15623](https://github.com/python/mypy/pull/15623)) -#### Documentation Updates +### Documentation Updates * Add documentation for additional error codes (Ivan Levkivskyi, PR [15539](https://github.com/python/mypy/pull/15539)) * Improve documentation of type narrowing (Ilya Priven, PR [15652](https://github.com/python/mypy/pull/15652)) * Small improvements to protocol documentation (Shantanu, PR [15460](https://github.com/python/mypy/pull/15460)) * Remove confusing instance variable example in cheat sheet (Adel Atallah, PR [15441](https://github.com/python/mypy/pull/15441)) -#### Other Notable Fixes and Improvements +### Other Notable Fixes and Improvements * Constant fold additional unary and binary expressions (Richard Si, PR [15202](https://github.com/python/mypy/pull/15202)) * Exclude the same special attributes from Protocol as CPython (Kyle Benesch, PR [15490](https://github.com/python/mypy/pull/15490)) @@ -1003,11 +1886,11 @@ Mypy and mypyc now support running on recent Python 3.12 development versions. N * Fix self types in subclass methods without Self annotation (Ivan Levkivskyi, PR [15541](https://github.com/python/mypy/pull/15541)) * Check for abstract class objects in tuples (Nikita Sobolev, PR [15366](https://github.com/python/mypy/pull/15366)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=fc7d4722eaa54803926cee5730e1f784979c0531+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1043,13 +1926,13 @@ We’ve just uploaded mypy 1.4 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### The Override Decorator +### The Override Decorator Mypy can now ensure that when renaming a method, overrides are also renamed. You can explicitly mark a method as overriding a base class method by using the @typing.override decorator ([PEP 698](https://peps.python.org/pep-0698/)). If the method is then renamed in the base class while the method override is not, mypy will generate an error. The decorator will be available in typing in Python 3.12, but you can also use the backport from a recent version of `typing_extensions` on all supported Python versions. This feature was contributed byThomas M Kehrenberg (PR [14609](https://github.com/python/mypy/pull/14609)). -#### Propagating Type Narrowing to Nested Functions +### Propagating Type Narrowing to Nested Functions Previously, type narrowing was not propagated to nested functions because it would not be sound if the narrowed variable changed between the definition of the nested function and the call site. Mypy will now propagate the narrowed type if the variable is not assigned to after the definition of the nested function: @@ -1069,7 +1952,7 @@ This may generate some new errors because asserts that were previously necessary This was contributed by Jukka Lehtosalo (PR [15133](https://github.com/python/mypy/pull/15133)). -#### Narrowing Enum Values Using “==” +### Narrowing Enum Values Using “==” Mypy now allows narrowing enum types using the \== operator. Previously this was only supported when using the is operator. This makes exhaustiveness checking with enum types more usable, as the requirement to use the is operator was not very intuitive. In this example mypy can detect that the developer forgot to handle the value MyEnum.C in example @@ -1138,18 +2021,18 @@ def test_something() -> None: This feature was contributed by Shantanu (PR [11521](https://github.com/python/mypy/pull/11521)). -#### Performance Improvements +### Performance Improvements * Speed up simplification of large union types and also fix a recursive tuple crash (Shantanu, PR [15128](https://github.com/python/mypy/pull/15128)) * Speed up union subtyping (Shantanu, PR [15104](https://github.com/python/mypy/pull/15104)) * Don't type check most function bodies when type checking third-party library code, or generally when ignoring errors (Jukka Lehtosalo, PR [14150](https://github.com/python/mypy/pull/14150)) -#### Improvements to Plugins +### Improvements to Plugins * attrs.evolve: Support generics and unions (Ilya Konstantinov, PR [15050](https://github.com/python/mypy/pull/15050)) * Fix ctypes plugin (Alex Waygood) -#### Fixes to Crashes +### Fixes to Crashes * Fix a crash when function-scope recursive alias appears as upper bound (Ivan Levkivskyi, PR [15159](https://github.com/python/mypy/pull/15159)) * Fix crash on follow\_imports\_for\_stubs (Ivan Levkivskyi, PR [15407](https://github.com/python/mypy/pull/15407)) @@ -1163,7 +2046,7 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Fix crash on lambda in generic context with generic method in body (Ivan Levkivskyi, PR [15155](https://github.com/python/mypy/pull/15155)) * Fix recursive type alias crash in make\_simplified\_union (Ivan Levkivskyi, PR [15216](https://github.com/python/mypy/pull/15216)) -#### Improvements to Error Messages +### Improvements to Error Messages * Use lower-case built-in collection types such as list\[…\] instead of List\[…\] in errors when targeting Python 3.9+ (Max Murin, PR [15070](https://github.com/python/mypy/pull/15070)) * Use X | Y union syntax in error messages when targeting Python 3.10+ (Omar Silva, PR [15102](https://github.com/python/mypy/pull/15102)) @@ -1176,7 +2059,7 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Add explanation if argument type is incompatible because of an unsupported numbers type (Jukka Lehtosalo, PR [15137](https://github.com/python/mypy/pull/15137)) * Add more detail to 'signature incompatible with supertype' messages for non-callables (Ilya Priven, PR [15263](https://github.com/python/mypy/pull/15263)) -#### Documentation Updates +### Documentation Updates * Add \--local-partial-types note to dmypy docs (Alan Du, PR [15259](https://github.com/python/mypy/pull/15259)) * Update getting started docs for mypyc for Windows (Valentin Stanciu, PR [15233](https://github.com/python/mypy/pull/15233)) @@ -1184,13 +2067,13 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Clarify difference between disallow\_untyped\_defs and disallow\_incomplete\_defs (Ilya Priven, PR [15247](https://github.com/python/mypy/pull/15247)) * Use attrs and @attrs.define in documentation and tests (Ilya Priven, PR [15152](https://github.com/python/mypy/pull/15152)) -#### Mypyc Improvements +### Mypyc Improvements * Fix unexpected TypeError for certain variables with an inferred optional type (Richard Si, PR [15206](https://github.com/python/mypy/pull/15206)) * Inline math literals (Logan Hunt, PR [15324](https://github.com/python/mypy/pull/15324)) * Support unpacking mappings in dict display (Richard Si, PR [15203](https://github.com/python/mypy/pull/15203)) -#### Changes to Stubgen +### Changes to Stubgen * Do not remove Generic from base classes (Ali Hamdan, PR [15316](https://github.com/python/mypy/pull/15316)) * Support yield from statements (Ali Hamdan, PR [15271](https://github.com/python/mypy/pull/15271)) @@ -1200,7 +2083,7 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Make stubgen respect MYPY\_CACHE\_DIR (Henrik Bäärnhielm, PR [14722](https://github.com/python/mypy/pull/14722)) * Fixes and simplifications (Ali Hamdan, PR [15232](https://github.com/python/mypy/pull/15232)) -#### Other Notable Fixes and Improvements +### Other Notable Fixes and Improvements * Fix nested async functions when using TypeVar value restriction (Jukka Lehtosalo, PR [14705](https://github.com/python/mypy/pull/14705)) * Always allow returning Any from lambda (Ivan Levkivskyi, PR [15413](https://github.com/python/mypy/pull/15413)) @@ -1213,11 +2096,11 @@ This feature was contributed by Shantanu (PR [11521](https://github.com/python/m * Fix match subject ignoring redefinitions (Vincent Vanlaer, PR [15306](https://github.com/python/mypy/pull/15306)) * Support `__all__`.remove (Shantanu, PR [15279](https://github.com/python/mypy/pull/15279)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=877e06ad1cfd9fd9967c0b0340a86d0c23ea89ce+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1273,12 +2156,12 @@ Posted by Jared Hance You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Performance Improvements +### Performance Improvements * Improve performance of union subtyping (Shantanu, PR [15104](https://github.com/python/mypy/pull/15104)) * Add negative subtype caches (Ivan Levkivskyi, PR [14884](https://github.com/python/mypy/pull/14884)) -#### Stub Tooling Improvements +### Stub Tooling Improvements * Stubtest: Check that the stub is abstract if the runtime is, even when the stub is an overloaded method (Alex Waygood, PR [14955](https://github.com/python/mypy/pull/14955)) * Stubtest: Verify stub methods or properties are decorated with @final if they are decorated with @final at runtime (Alex Waygood, PR [14951](https://github.com/python/mypy/pull/14951)) @@ -1286,12 +2169,12 @@ You can read the full documentation for this release on [Read the Docs](http://m * Stubgen: Support @functools.cached\_property (Nikita Sobolev, PR [14981](https://github.com/python/mypy/pull/14981)) * Improvements to stubgenc (Chad Dombrova, PR [14564](https://github.com/python/mypy/pull/14564)) -#### Improvements to attrs +### Improvements to attrs * Add support for converters with TypeVars on generic attrs classes (Chad Dombrova, PR [14908](https://github.com/python/mypy/pull/14908)) * Fix attrs.evolve on bound TypeVar (Ilya Konstantinov, PR [15022](https://github.com/python/mypy/pull/15022)) -#### Documentation Updates +### Documentation Updates * Improve async documentation (Shantanu, PR [14973](https://github.com/python/mypy/pull/14973)) * Improvements to cheat sheet (Shantanu, PR [14972](https://github.com/python/mypy/pull/14972)) @@ -1303,26 +2186,26 @@ You can read the full documentation for this release on [Read the Docs](http://m * Fix alignment of cheat sheet example (Ondřej Cvacho, PR [15039](https://github.com/python/mypy/pull/15039)) * Fix error for callback protocol matching against callable type object (Shantanu, PR [15042](https://github.com/python/mypy/pull/15042)) -#### Error Reporting Improvements +### Error Reporting Improvements * Improve bytes formatting error (Shantanu, PR [14959](https://github.com/python/mypy/pull/14959)) -#### Mypyc Improvements +### Mypyc Improvements * Fix unions of bools and ints (Tomer Chachamu, PR [15066](https://github.com/python/mypy/pull/15066)) -#### Other Fixes and Improvements +### Other Fixes and Improvements * Fix narrowing union types that include Self with isinstance (Christoph Tyralla, PR [14923](https://github.com/python/mypy/pull/14923)) * Allow objects matching SupportsKeysAndGetItem to be unpacked (Bryan Forbes, PR [14990](https://github.com/python/mypy/pull/14990)) * Check type guard validity for staticmethods (EXPLOSION, PR [14953](https://github.com/python/mypy/pull/14953)) * Fix sys.platform when cross-compiling with emscripten (Ethan Smith, PR [14888](https://github.com/python/mypy/pull/14888)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=b0ed50e9392a23e52445b630a808153e0e256976+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1361,14 +2244,14 @@ We’ve just uploaded mypy 1.2 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Improvements to Dataclass Transforms +### Improvements to Dataclass Transforms * Support implicit default for "init" parameter in field specifiers (Wesley Collin Wright and Jukka Lehtosalo, PR [15010](https://github.com/python/mypy/pull/15010)) * Support descriptors in dataclass transform (Jukka Lehtosalo, PR [15006](https://github.com/python/mypy/pull/15006)) * Fix frozen\_default in incremental mode (Wesley Collin Wright) * Fix frozen behavior for base classes with direct metaclasses (Wesley Collin Wright, PR [14878](https://github.com/python/mypy/pull/14878)) -#### Mypyc: Native Floats +### Mypyc: Native Floats Mypyc now uses a native, unboxed representation for values of type float. Previously these were heap-allocated Python objects. Native floats are faster and use less memory. Code that uses floating-point operations heavily can be several times faster when using native floats. @@ -1407,7 +2290,7 @@ Related changes: * Document native floats and integers (Jukka Lehtosalo, PR [14927](https://github.com/python/mypy/pull/14927)) * Fixes to float to int conversion (Jukka Lehtosalo, PR [14936](https://github.com/python/mypy/pull/14936)) -#### Mypyc: Native Integers +### Mypyc: Native Integers Mypyc now supports signed 32-bit and 64-bit integer types in addition to the arbitrary-precision int type. You can use the types mypy\_extensions.i32 and mypy\_extensions.i64 to speed up code that uses integer operations heavily. @@ -1421,33 +2304,33 @@ def inc(x: i64) -> i64: Refer to the [documentation](https://mypyc.readthedocs.io/en/latest/using_type_annotations.html#native-integer-types) for more information. This feature was contributed by Jukka Lehtosalo. -#### Other Mypyc Fixes and Improvements +### Other Mypyc Fixes and Improvements * Support iterating over a TypedDict (Richard Si, PR [14747](https://github.com/python/mypy/pull/14747)) * Faster coercions between different tuple types (Jukka Lehtosalo, PR [14899](https://github.com/python/mypy/pull/14899)) * Faster calls via type aliases (Jukka Lehtosalo, PR [14784](https://github.com/python/mypy/pull/14784)) * Faster classmethod calls via cls (Jukka Lehtosalo, PR [14789](https://github.com/python/mypy/pull/14789)) -#### Fixes to Crashes +### Fixes to Crashes * Fix crash on class-level import in protocol definition (Ivan Levkivskyi, PR [14926](https://github.com/python/mypy/pull/14926)) * Fix crash on single item union of alias (Ivan Levkivskyi, PR [14876](https://github.com/python/mypy/pull/14876)) * Fix crash on ParamSpec in incremental mode (Ivan Levkivskyi, PR [14885](https://github.com/python/mypy/pull/14885)) -#### Documentation Updates +### Documentation Updates * Update adopting \--strict documentation for 1.0 (Shantanu, PR [14865](https://github.com/python/mypy/pull/14865)) * Some minor documentation tweaks (Jukka Lehtosalo, PR [14847](https://github.com/python/mypy/pull/14847)) * Improve documentation of top level mypy: disable-error-code comment (Nikita Sobolev, PR [14810](https://github.com/python/mypy/pull/14810)) -#### Error Reporting Improvements +### Error Reporting Improvements * Add error code to `typing_extensions` suggestion (Shantanu, PR [14881](https://github.com/python/mypy/pull/14881)) * Add a separate error code for top-level await (Nikita Sobolev, PR [14801](https://github.com/python/mypy/pull/14801)) * Don’t suggest two obsolete stub packages (Jelle Zijlstra, PR [14842](https://github.com/python/mypy/pull/14842)) * Add suggestions for pandas-stubs and lxml-stubs (Shantanu, PR [14737](https://github.com/python/mypy/pull/14737)) -#### Other Fixes and Improvements +### Other Fixes and Improvements * Multiple inheritance considers callable objects as subtypes of functions (Christoph Tyralla, PR [14855](https://github.com/python/mypy/pull/14855)) * stubtest: Respect @final runtime decorator and enforce it in stubs (Nikita Sobolev, PR [14922](https://github.com/python/mypy/pull/14922)) @@ -1466,11 +2349,11 @@ Refer to the [documentation](https://mypyc.readthedocs.io/en/latest/using_type_a * Improve “used before definition” checks when a local definition has the same name as a global definition (Stas Ilinskiy, PR [14517](https://github.com/python/mypy/pull/14517)) * Honor NoReturn as \_\_setitem\_\_ return type to mark unreachable code (sterliakov, PR [12572](https://github.com/python/mypy/pull/12572)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=a544b75320e97424d2d927605316383c755cdac0+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1506,13 +2389,13 @@ Posted by Jukka Lehtosalo You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### Support for `dataclass_transform`` +### Support for `dataclass_transform`` This release adds full support for the dataclass\_transform decorator defined in [PEP 681](https://peps.python.org/pep-0681/#decorator-function-example). This allows decorators, base classes, and metaclasses that generate a \_\_init\_\_ method or other methods based on the properties of that class (similar to dataclasses) to have those methods recognized by mypy. This was contributed by Wesley Collin Wright. -#### Dedicated Error Code for Method Assignments +### Dedicated Error Code for Method Assignments Mypy can’t safely check all assignments to methods (a form of monkey patching), so mypy generates an error by default. To make it easier to ignore this error, mypy now uses the new error code method-assign for this. By disabling this error code in a file or globally, mypy will no longer complain about assignments to methods if the signatures are compatible. @@ -1520,16 +2403,16 @@ Mypy also supports the old error code assignment for these assignments to preven This was contributed by Ivan Levkivskyi (PR [14570](https://github.com/python/mypy/pull/14570)). -#### Fixes to Crashes +### Fixes to Crashes * Fix a crash on walrus in comprehension at class scope (Ivan Levkivskyi, PR [14556](https://github.com/python/mypy/pull/14556)) * Fix crash related to value-constrained TypeVar (Shantanu, PR [14642](https://github.com/python/mypy/pull/14642)) -#### Fixes to Cache Corruption +### Fixes to Cache Corruption * Fix generic TypedDict/NamedTuple caching (Ivan Levkivskyi, PR [14675](https://github.com/python/mypy/pull/14675)) -#### Mypyc Fixes and Improvements +### Mypyc Fixes and Improvements * Raise "non-trait base must be first..." error less frequently (Richard Si, PR [14468](https://github.com/python/mypy/pull/14468)) * Generate faster code for bool comparisons and arithmetic (Jukka Lehtosalo, PR [14489](https://github.com/python/mypy/pull/14489)) @@ -1540,12 +2423,12 @@ This was contributed by Ivan Levkivskyi (PR [14570](https://github.com/python/my * Fix crash on star unpacking to underscore (Ivan Levkivskyi, PR [14624](https://github.com/python/mypy/pull/14624)) * Fix iterating over a union of dicts (Richard Si, PR [14713](https://github.com/python/mypy/pull/14713)) -#### Fixes to Detecting Undefined Names (used-before-def) +### Fixes to Detecting Undefined Names (used-before-def) * Correctly handle walrus operator (Stas Ilinskiy, PR [14646](https://github.com/python/mypy/pull/14646)) * Handle walrus declaration in match subject correctly (Stas Ilinskiy, PR [14665](https://github.com/python/mypy/pull/14665)) -#### Stubgen Improvements +### Stubgen Improvements Stubgen is a tool for automatically generating draft stubs for libraries. @@ -1553,14 +2436,14 @@ Stubgen is a tool for automatically generating draft stubs for libraries. * Fix crash with PEP 604 union in type variable bound (Shantanu, PR [14557](https://github.com/python/mypy/pull/14557)) * Preserve PEP 604 unions in generated .pyi files (hamdanal, PR [14601](https://github.com/python/mypy/pull/14601)) -#### Stubtest Improvements +### Stubtest Improvements Stubtest is a tool for testing that stubs conform to the implementations. * Update message format so that it’s easier to go to error location (Avasam, PR [14437](https://github.com/python/mypy/pull/14437)) * Handle name-mangling edge cases better (Alex Waygood, PR [14596](https://github.com/python/mypy/pull/14596)) -#### Changes to Error Reporting and Messages +### Changes to Error Reporting and Messages * Add new TypedDict error code typeddict-unknown-key (JoaquimEsteves, PR [14225](https://github.com/python/mypy/pull/14225)) * Give arguments a more reasonable location in error messages (Max Murin, PR [14562](https://github.com/python/mypy/pull/14562)) @@ -1572,7 +2455,7 @@ Stubtest is a tool for testing that stubs conform to the implementations. * Adjust inconsistent dataclasses plugin error messages (Wesley Collin Wright, PR [14637](https://github.com/python/mypy/pull/14637)) * Consolidate literal bool argument error messages (Wesley Collin Wright, PR [14693](https://github.com/python/mypy/pull/14693)) -#### Other Fixes and Improvements +### Other Fixes and Improvements * Check that type guards accept a positional argument (EXPLOSION, PR [14238](https://github.com/python/mypy/pull/14238)) * Fix bug with in operator used with a union of Container and Iterable (Max Murin, PR [14384](https://github.com/python/mypy/pull/14384)) @@ -1580,11 +2463,11 @@ Stubtest is a tool for testing that stubs conform to the implementations. * Allow overlapping comparisons between bytes-like types (Shantanu, PR [14658](https://github.com/python/mypy/pull/14658)) * Fix mypy daemon documentation link in README (Ivan Levkivskyi, PR [14644](https://github.com/python/mypy/pull/14644)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=5ebf892d0710a6e87925b8d138dfa597e7bb11cc+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: @@ -1626,7 +2509,7 @@ We’ve just uploaded mypy 1.0 to the Python Package Index ([PyPI](https://pypi. You can read the full documentation for this release on [Read the Docs](http://mypy.readthedocs.io). -#### New Release Versioning Scheme +### New Release Versioning Scheme Now that mypy reached 1.0, we’ll switch to a new versioning scheme. Mypy version numbers will be of form x.y.z. @@ -1642,7 +2525,7 @@ Any significant backward incompatible change must be announced in the blog post See [”Release Process” in the mypy wiki](https://github.com/python/mypy/wiki/Release-Process) for more details and for the most up-to-date version of the versioning scheme. -#### Performance Improvements +### Performance Improvements Mypy 1.0 is up to 40% faster than mypy 0.991 when type checking the Dropbox internal codebase. We also set up a daily job to measure the performance of the most recent development version of mypy to make it easier to track changes in performance. @@ -1669,7 +2552,7 @@ Many optimizations contributed to this improvement: * Speed up freshening type variables (Jukka Lehtosalo, PR [14323](https://github.com/python/mypy/pull/14323)) * Optimize implementation of TypedDict types for \*\*kwds (Jukka Lehtosalo, PR [14316](https://github.com/python/mypy/pull/14316)) -#### Warn About Variables Used Before Definition +### Warn About Variables Used Before Definition Mypy will now generate an error if you use a variable before it’s defined. This feature is enabled by default. By default mypy reports an error when it infers that a variable is always undefined. ```python @@ -1678,7 +2561,7 @@ x = 0 ``` This feature was contributed by Stas Ilinskiy. -#### Detect Possibly Undefined Variables (Experimental) +### Detect Possibly Undefined Variables (Experimental) A new experimental possibly-undefined error code is now available that will detect variables that may be undefined: ```python @@ -1690,7 +2573,7 @@ The error code is disabled be default, since it can generate false positives. This feature was contributed by Stas Ilinskiy. -#### Support the “Self” Type +### Support the “Self” Type There is now a simpler syntax for declaring [generic self types](https://mypy.readthedocs.io/en/stable/generics.html#generic-methods-and-generic-self) introduced in [PEP 673](https://peps.python.org/pep-0673/): the Self type. You no longer have to define a type variable to use “self types”, and you can use them with attributes. Example from mypy documentation: ```python @@ -1716,7 +2599,7 @@ The feature was introduced in Python 3.11. In earlier Python versions a backport This was contributed by Ivan Levkivskyi (PR [14041](https://github.com/python/mypy/pull/14041)). -#### Support ParamSpec in Type Aliases +### Support ParamSpec in Type Aliases ParamSpec and Concatenate can now be used in type aliases. Example: ```python @@ -1730,11 +2613,11 @@ def f(c: A[int, str]) -> None: ``` This feature was contributed by Ivan Levkivskyi (PR [14159](https://github.com/python/mypy/pull/14159)). -#### ParamSpec and Generic Self Types No Longer Experimental +### ParamSpec and Generic Self Types No Longer Experimental Support for ParamSpec ([PEP 612](https://www.python.org/dev/peps/pep-0612/)) and generic self types are no longer considered experimental. -#### Miscellaneous New Features +### Miscellaneous New Features * Minimal, partial implementation of dataclass\_transform ([PEP 681](https://peps.python.org/pep-0681/)) (Wesley Collin Wright, PR [14523](https://github.com/python/mypy/pull/14523)) * Add basic support for `typing_extensions`.TypeVar (Marc Mueller, PR [14313](https://github.com/python/mypy/pull/14313)) @@ -1747,7 +2630,7 @@ Support for ParamSpec ([PEP 612](https://www.python.org/dev/peps/pep-0612/)) and * Generate error for class attribute access if attribute is defined with `__slots__` (Harrison McCarty, PR [14125](https://github.com/python/mypy/pull/14125)) * Support additional attributes in callback protocols (Ivan Levkivskyi, PR [14084](https://github.com/python/mypy/pull/14084)) -#### Fixes to Crashes +### Fixes to Crashes * Fix crash on prefixed ParamSpec with forward reference (Ivan Levkivskyi, PR [14569](https://github.com/python/mypy/pull/14569)) * Fix internal crash when resolving the same partial type twice (Shantanu, PR [14552](https://github.com/python/mypy/pull/14552)) @@ -1767,19 +2650,19 @@ Support for ParamSpec ([PEP 612](https://www.python.org/dev/peps/pep-0612/)) and * Fix crash with enums (Michael Lee, PR [14021](https://github.com/python/mypy/pull/14021)) * Fix crash with malformed TypedDicts and disllow-any-expr (Michael Lee, PR [13963](https://github.com/python/mypy/pull/13963)) -#### Error Reporting Improvements +### Error Reporting Improvements * More helpful error for missing self (Shantanu, PR [14386](https://github.com/python/mypy/pull/14386)) * Add error-code truthy-iterable (Marc Mueller, PR [13762](https://github.com/python/mypy/pull/13762)) * Fix pluralization in error messages (KotlinIsland, PR [14411](https://github.com/python/mypy/pull/14411)) -#### Mypyc: Support Match Statement +### Mypyc: Support Match Statement Mypyc can now compile Python 3.10 match statements. This was contributed by dosisod (PR [13953](https://github.com/python/mypy/pull/13953)). -#### Other Mypyc Fixes and Improvements +### Other Mypyc Fixes and Improvements * Optimize int(x)/float(x)/complex(x) on instances of native classes (Richard Si, PR [14450](https://github.com/python/mypy/pull/14450)) * Always emit warnings (Richard Si, PR [14451](https://github.com/python/mypy/pull/14451)) @@ -1795,7 +2678,7 @@ This was contributed by dosisod (PR [13953](https://github.com/python/mypy/pull/ * Allow use of enum.Enum (Shantanu, PR [13995](https://github.com/python/mypy/pull/13995)) * Fix compiling on Arch Linux (dosisod, PR [13978](https://github.com/python/mypy/pull/13978)) -#### Documentation Improvements +### Documentation Improvements * Various documentation and error message tweaks (Jukka Lehtosalo, PR [14574](https://github.com/python/mypy/pull/14574)) * Improve Generics documentation (Shantanu, PR [14587](https://github.com/python/mypy/pull/14587)) @@ -1815,7 +2698,7 @@ This was contributed by dosisod (PR [13953](https://github.com/python/mypy/pull/ * Flycheck-mypy is deprecated, since its functionality was merged to Flycheck (Ivan Levkivskyi, PR [14247](https://github.com/python/mypy/pull/14247)) * Update code example in "Declaring decorators" (ChristianWitzler, PR [14131](https://github.com/python/mypy/pull/14131)) -#### Stubtest Improvements +### Stubtest Improvements Stubtest is a tool for testing that stubs conform to the implementations. @@ -1826,13 +2709,13 @@ Stubtest is a tool for testing that stubs conform to the implementations. * Add \_\_warningregistry\_\_ to the list of ignored module dunders (Nikita Sobolev, PR [14218](https://github.com/python/mypy/pull/14218)) * If a default is present in the stub, check that it is correct (Jelle Zijlstra, PR [14085](https://github.com/python/mypy/pull/14085)) -#### Stubgen Improvements +### Stubgen Improvements Stubgen is a tool for automatically generating draft stubs for libraries. * Treat dlls as C modules (Shantanu, PR [14503](https://github.com/python/mypy/pull/14503)) -#### Other Notable Fixes and Improvements +### Other Notable Fixes and Improvements * Update stub suggestions based on recent typeshed changes (Alex Waygood, PR [14265](https://github.com/python/mypy/pull/14265)) * Fix attrs protocol check with cache (Marc Mueller, PR [14558](https://github.com/python/mypy/pull/14558)) @@ -1871,11 +2754,11 @@ Stubgen is a tool for automatically generating draft stubs for libraries. * Improve handling of redefinitions through imports (Shantanu, PR [13969](https://github.com/python/mypy/pull/13969)) * Preserve (some) implicitly exported types (Shantanu, PR [13967](https://github.com/python/mypy/pull/13967)) -#### Typeshed Updates +### Typeshed Updates Typeshed is now modular and distributed as separate PyPI packages for everything except the standard library stubs. Please see [git log](https://github.com/python/typeshed/commits/main?after=ea0ae2155e8a04c9837903c3aff8dd5ad5f36ebc+0&branch=main&path=stdlib) for full list of typeshed changes. -#### Acknowledgements +### Acknowledgements Thanks to all mypy contributors who contributed to this release: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5d339330a75a..24f7e516e9e26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,16 +65,16 @@ However, if you wish to do so, you can run the full test suite like this: ```bash -python3 runtests.py +python runtests.py ``` Some useful commands for running specific tests include: ```bash # Use mypy to check mypy's own code -python3 runtests.py self +python runtests.py self # or equivalently: -python3 -m mypy --config-file mypy_self_check.ini -p mypy +python -m mypy --config-file mypy_self_check.ini -p mypy # Run a single test from the test suite pytest -n0 -k 'test_name' @@ -117,7 +117,7 @@ tox -e dev --override testenv:dev.allowlist_externals+=env -- env # inspect the ``` If you don't already have `tox` installed, you can use a virtual environment as -described above to install `tox` via `pip` (e.g., ``python3 -m pip install tox``). +described above to install `tox` via `pip` (e.g., ``python -m pip install tox``). ## First time contributors diff --git a/MANIFEST.in b/MANIFEST.in index c18b83cc0088a..c2399d2b00b67 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -41,8 +41,8 @@ include runtests.py include pytest.ini include tox.ini -include LICENSE mypyc/README.md -exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md CHANGELOG.md action.yml .editorconfig +include LICENSE mypyc/README.md CHANGELOG.md +exclude .gitmodules CONTRIBUTING.md CREDITS ROADMAP.md action.yml .editorconfig exclude .git-blame-ignore-revs .pre-commit-config.yaml global-exclude *.py[cod] diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index a3504b07824dd..a94c1b7ba95c1 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,2 +1,3 @@ -sphinx>=5.1.0 +sphinx>=8.1.0 furo>=2022.3.4 +myst-parser>=4.0.0 diff --git a/docs/source/additional_features.rst b/docs/source/additional_features.rst index ae625c1576540..e7c162a0b0df9 100644 --- a/docs/source/additional_features.rst +++ b/docs/source/additional_features.rst @@ -46,21 +46,18 @@ define dataclasses. For example: UnorderedPoint(1, 2) < UnorderedPoint(3, 4) # Error: Unsupported operand types Dataclasses can be generic and can be used in any other way a normal -class can be used: +class can be used (Python 3.12 syntax): .. code-block:: python from dataclasses import dataclass - from typing import Generic, TypeVar - - T = TypeVar('T') @dataclass - class BoxedData(Generic[T]): + class BoxedData[T]: data: T label: str - def unbox(bd: BoxedData[T]) -> T: + def unbox[T](bd: BoxedData[T]) -> T: ... val = unbox(BoxedData(42, "")) # OK, inferred type is int @@ -98,17 +95,16 @@ does **not** work: To have Mypy recognize a wrapper of :py:func:`dataclasses.dataclass ` -as a dataclass decorator, consider using the :py:func:`~typing.dataclass_transform` decorator: +as a dataclass decorator, consider using the :py:func:`~typing.dataclass_transform` +decorator (example uses Python 3.12 syntax): .. code-block:: python from dataclasses import dataclass, Field - from typing import TypeVar, dataclass_transform - - T = TypeVar('T') + from typing import dataclass_transform @dataclass_transform(field_specifiers=(Field,)) - def my_dataclass(cls: type[T]) -> type[T]: + def my_dataclass[T](cls: type[T]) -> type[T]: ... return dataclass(cls) @@ -367,20 +363,20 @@ Extended Callable types This feature is deprecated. You can use :ref:`callback protocols ` as a replacement. -As an experimental mypy extension, you can specify :py:data:`~typing.Callable` types +As an experimental mypy extension, you can specify :py:class:`~collections.abc.Callable` types that support keyword arguments, optional arguments, and more. When -you specify the arguments of a :py:data:`~typing.Callable`, you can choose to supply just +you specify the arguments of a :py:class:`~collections.abc.Callable`, you can choose to supply just the type of a nameless positional argument, or an "argument specifier" representing a more complicated form of argument. This allows one to more closely emulate the full range of possibilities given by the ``def`` statement in Python. As an example, here's a complicated function definition and the -corresponding :py:data:`~typing.Callable`: +corresponding :py:class:`~collections.abc.Callable`: .. code-block:: python - from typing import Callable + from collections.abc import Callable from mypy_extensions import (Arg, DefaultArg, NamedArg, DefaultNamedArg, VarArg, KwArg) @@ -453,7 +449,7 @@ purpose: In all cases, the ``type`` argument defaults to ``Any``, and if the ``name`` argument is omitted the argument has no name (the name is required for ``NamedArg`` and ``DefaultNamedArg``). A basic -:py:data:`~typing.Callable` such as +:py:class:`~collections.abc.Callable` such as .. code-block:: python @@ -465,7 +461,7 @@ is equivalent to the following: MyFunc = Callable[[Arg(int), Arg(str), Arg(int)], float] -A :py:data:`~typing.Callable` with unspecified argument types, such as +A :py:class:`~collections.abc.Callable` with unspecified argument types, such as .. code-block:: python diff --git a/docs/source/changelog.md b/docs/source/changelog.md new file mode 100644 index 0000000000000..a490ada727a69 --- /dev/null +++ b/docs/source/changelog.md @@ -0,0 +1,3 @@ + +```{include} ../../CHANGELOG.md +``` diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index b8e43960fd09f..7385a66863bf6 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -72,9 +72,9 @@ Useful built-in types # On earlier versions, use Union x: list[Union[int, str]] = [3, 5, "test", "fun"] - # Use Optional[X] for a value that could be None - # Optional[X] is the same as X | None or Union[X, None] - x: Optional[str] = "something" if some_condition() else None + # Use X | None for a value that could be None on Python 3.10+ + # Use Optional[X] on 3.9 and earlier; Optional[X] is the same as 'X | None' + x: str | None = "something" if some_condition() else None if x is not None: # Mypy understands x won't be None here because of the if-statement print(x.upper()) @@ -88,7 +88,8 @@ Functions .. code-block:: python - from typing import Callable, Iterator, Union, Optional + from collections.abc import Iterator, Callable + from typing import Union, Optional # This is how you annotate a function definition def stringify(num: int) -> str: @@ -121,13 +122,14 @@ Functions i += 1 # You can of course split a function annotation over multiple lines - def send_email(address: Union[str, list[str]], - sender: str, - cc: Optional[list[str]], - bcc: Optional[list[str]], - subject: str = '', - body: Optional[list[str]] = None - ) -> bool: + def send_email( + address: str | list[str], + sender: str, + cc: list[str] | None, + bcc: list[str] | None, + subject: str = '', + body: list[str] | None = None, + ) -> bool: ... # Mypy understands positional-only and keyword-only arguments @@ -230,7 +232,7 @@ When you're puzzled or when things are complicated # If you initialize a variable with an empty container or "None" # you may have to help mypy a bit by providing an explicit type annotation x: list[str] = [] - x: Optional[str] = None + x: str | None = None # Use Any if you don't know the type of something or it's too # dynamic to write a type for @@ -274,7 +276,8 @@ that are common in idiomatic Python are standardized. .. code-block:: python - from typing import Mapping, MutableMapping, Sequence, Iterable + from collections.abc import Mapping, MutableMapping, Sequence, Iterable + # or 'from typing import ...' (required in Python 3.8) # Use Iterable for generic iterables (anything usable in "for"), # and Sequence where a sequence (supporting "len" and "__getitem__") is @@ -349,11 +352,26 @@ Decorators ********** Decorator functions can be expressed via generics. See -:ref:`declaring-decorators` for more details. +:ref:`declaring-decorators` for more details. Example using Python 3.12 +syntax: + +.. code-block:: python + + from collections.abc import Callable + from typing import Any + + def bare_decorator[F: Callable[..., Any]](func: F) -> F: + ... + + def decorator_args[F: Callable[..., Any]](url: str) -> Callable[[F], F]: + ... + +The same example using pre-3.12 syntax: .. code-block:: python - from typing import Any, Callable, TypeVar + from collections.abc import Callable + from typing import Any, TypeVar F = TypeVar('F', bound=Callable[..., Any]) diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst index 1d80da5830ec9..241dbeae0f447 100644 --- a/docs/source/class_basics.rst +++ b/docs/source/class_basics.rst @@ -152,7 +152,8 @@ between class and instance variables with callable types. For example: .. code-block:: python - from typing import Callable, ClassVar + from collections.abc import Callable + from typing import ClassVar class A: foo: Callable[[int], None] diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 50a6ef65f4d08..ea96e9f647903 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -96,6 +96,10 @@ Optional arguments Show program's version number and exit. +.. option:: -O FORMAT, --output FORMAT {json} + + Set a custom output format. + .. _config-file-flag: Config file @@ -162,6 +166,17 @@ imports. For more details, see :ref:`ignore-missing-imports`. +.. option:: --follow-untyped-imports + + This flag makes mypy analyze imports from installed packages even if + missing a :ref:`py.typed marker or stubs `. + + .. warning:: + + Note that analyzing all unannotated modules might result in issues + when analyzing code not designed to be type checked and may significantly + increase how long mypy takes to run. + .. option:: --follow-imports {normal,silent,skip,error} This flag adjusts how mypy follows imported modules that were not @@ -420,11 +435,11 @@ The following flags adjust how mypy handles values of type ``None``. .. option:: --implicit-optional - This flag causes mypy to treat arguments with a ``None`` - default value as having an implicit :py:data:`~typing.Optional` type. + This flag causes mypy to treat parameters with a ``None`` + default value as having an implicit optional type (``T | None``). For example, if this flag is set, mypy would assume that the ``x`` - parameter is actually of type ``Optional[int]`` in the code snippet below + parameter is actually of type ``int | None`` in the code snippet below, since the default parameter is ``None``: .. code-block:: python @@ -438,7 +453,7 @@ The following flags adjust how mypy handles values of type ``None``. .. option:: --no-strict-optional - This flag effectively disables checking of :py:data:`~typing.Optional` + This flag effectively disables checking of optional types and ``None`` values. With this option, mypy doesn't generally check the use of ``None`` values -- it is treated as compatible with every type. @@ -533,6 +548,12 @@ potentially problematic or redundant in some way. This limitation will be removed in future releases of mypy. +.. option:: --report-deprecated-as-note + + If error code ``deprecated`` is enabled, mypy emits errors if your code + imports or uses deprecated features. This flag converts such errors to + notes, causing mypy to eventually finish with a zero exit code. Features + are considered deprecated when decorated with ``warnings.deprecated``. .. _miscellaneous-strictness-flags: @@ -575,26 +596,24 @@ of the above sections. .. option:: --local-partial-types In mypy, the most common cases for partial types are variables initialized using ``None``, - but without explicit ``Optional`` annotations. By default, mypy won't check partial types + but without explicit ``X | None`` annotations. By default, mypy won't check partial types spanning module top level or class top level. This flag changes the behavior to only allow partial types at local level, therefore it disallows inferring variable type for ``None`` from two assignments in different scopes. For example: .. code-block:: python - from typing import Optional - a = None # Need type annotation here if using --local-partial-types - b: Optional[int] = None + b: int | None = None class Foo: bar = None # Need type annotation here if using --local-partial-types - baz: Optional[int] = None + baz: int | None = None def __init__(self) -> None: self.bar = 1 - reveal_type(Foo().bar) # Union[int, None] without --local-partial-types + reveal_type(Foo().bar) # 'int | None' without --local-partial-types Note: this option is always implicitly enabled in mypy daemon and will become enabled by default for mypy in a future release. @@ -630,13 +649,11 @@ of the above sections. .. code-block:: python - from typing import Text - items: list[int] if 'some string' in items: # Error: non-overlapping container check! ... - text: Text + text: str if text != b'other bytes': # Error: non-overlapping equality check! ... @@ -1008,7 +1025,7 @@ format into the specified directory. Enabling incomplete/experimental features ***************************************** -.. option:: --enable-incomplete-feature {PreciseTupleTypes} +.. option:: --enable-incomplete-feature {PreciseTupleTypes, InlineTypedDict} Some features may require several mypy releases to implement, for example due to their complexity, potential for backwards incompatibility, or @@ -1055,6 +1072,14 @@ List of currently incomplete/experimental features: # Without PreciseTupleTypes: tuple[int, ...] # With PreciseTupleTypes: tuple[()] | tuple[int] | tuple[int, int] +* ``InlineTypedDict``: this feature enables non-standard syntax for inline + :ref:`TypedDicts `, for example: + + .. code-block:: python + + def test_values() -> {"int": int, "str": str}: + return {"int": 42, "str": "test"} + Miscellaneous ************* diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index cfe82e19e77b8..39954b8e332a0 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -363,7 +363,8 @@ explicit type cast: .. code-block:: python - from typing import Sequence, cast + from collections.abc import Sequence + from typing import cast def find_first_str(a: Sequence[object]) -> str: index = next((i for i, s in enumerate(a) if isinstance(s, str)), -1) @@ -700,7 +701,7 @@ This example demonstrates both safe and unsafe overrides: .. code-block:: python - from typing import Sequence, List, Iterable + from collections.abc import Sequence, Iterable class A: def test(self, t: Sequence[int]) -> Sequence[str]: @@ -713,7 +714,7 @@ This example demonstrates both safe and unsafe overrides: class NarrowerArgument(A): # A more specific argument type isn't accepted - def test(self, t: List[int]) -> Sequence[str]: # Error + def test(self, t: list[int]) -> Sequence[str]: # Error ... class NarrowerReturn(A): @@ -802,7 +803,7 @@ This is best understood via an example: .. code-block:: python - def foo(x: Optional[int]) -> Callable[[], int]: + def foo(x: int | None) -> Callable[[], int]: if x is None: x = 5 print(x + 1) # mypy correctly deduces x must be an int here diff --git a/docs/source/conf.py b/docs/source/conf.py index fa76734054acc..ddc9923c6c93a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -35,7 +35,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["sphinx.ext.intersphinx", "docs.source.html_builder"] +extensions = ["sphinx.ext.intersphinx", "docs.source.html_builder", "myst_parser"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -51,7 +51,7 @@ # General information about the project. project = "mypy" -copyright = "2012-2022 Jukka Lehtosalo and mypy contributors" +copyright = "2012-%Y Jukka Lehtosalo and mypy contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -92,7 +92,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" +# pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -107,6 +107,12 @@ # a list of builtin themes. html_theme = "furo" +html_theme_options = { + "source_repository": "https://github.com/python/mypy", + "source_branch": "master", + "source_directory": "docs/source", +} + # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index b0e82a33255a6..d7ae1b7a00df2 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -315,6 +315,24 @@ section of the command line docs. match the name of the *imported* module, not the module containing the import statement. +.. confval:: follow_untyped_imports + + :type: boolean + :default: False + + Makes mypy analyze imports from installed packages even if missing a + :ref:`py.typed marker or stubs `. + + If this option is used in a per-module section, the module name should + match the name of the *imported* module, not the module containing the + import statement. + + .. warning:: + + Note that analyzing all unannotated modules might result in issues + when analyzing code not designed to be type checked and may significantly + increase how long mypy takes to run. + .. confval:: follow_imports :type: string @@ -574,8 +592,8 @@ section of the command line docs. :type: boolean :default: False - Causes mypy to treat arguments with a ``None`` - default value as having an implicit :py:data:`~typing.Optional` type. + Causes mypy to treat parameters with a ``None`` + default value as having an implicit optional type (``T | None``). **Note:** This was True by default in mypy versions 0.980 and earlier. @@ -584,7 +602,7 @@ section of the command line docs. :type: boolean :default: True - Effectively disables checking of :py:data:`~typing.Optional` + Effectively disables checking of optional types and ``None`` values. With this option, mypy doesn't generally check the use of ``None`` values -- it is treated as compatible with every type. @@ -717,6 +735,14 @@ section of the command line docs. Note: This option will override disabled error codes from the disable_error_code option. +.. confval:: extra_checks + + :type: boolean + :default: False + + This flag enables additional checks that are technically correct but may be impractical in real code. + See :option:`mypy --extra-checks` for more info. + .. confval:: implicit_reexport :type: boolean @@ -739,23 +765,23 @@ section of the command line docs. .. confval:: strict_concatenate - :type: boolean - :default: False + :type: boolean + :default: False - Make arguments prepended via ``Concatenate`` be truly positional-only. + Make arguments prepended via ``Concatenate`` be truly positional-only. .. confval:: strict_equality - :type: boolean - :default: False + :type: boolean + :default: False Prohibit equality checks, identity checks, and container checks between non-overlapping types. .. confval:: strict - :type: boolean - :default: False + :type: boolean + :default: False Enable all optional error checking flags. You can see the list of flags enabled by strict mode in the full :option:`mypy --help` diff --git a/docs/source/dynamic_typing.rst b/docs/source/dynamic_typing.rst index d3476de2ca641..304e25c085a83 100644 --- a/docs/source/dynamic_typing.rst +++ b/docs/source/dynamic_typing.rst @@ -81,14 +81,12 @@ treated as ``Any``: .. code-block:: python - from typing import List - - def f(x: List) -> None: + def f(x: list) -> None: reveal_type(x) # Revealed type is "builtins.list[Any]" reveal_type(x[0]) # Revealed type is "Any" x[0].anything_goes() # OK -You can make mypy warn you about untyped function parameters using the +You can make mypy warn you about missing generic parameters using the :option:`--disallow-any-generics ` flag. Finally, another major source of ``Any`` types leaking into your program is from diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 64d9a1d032875..73171131bc8d9 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -59,8 +59,6 @@ Example: .. code-block:: python - from typing import Union - class Cat: def sleep(self) -> None: ... def miaow(self) -> None: ... @@ -69,10 +67,10 @@ Example: def sleep(self) -> None: ... def follow_me(self) -> None: ... - def func(animal: Union[Cat, Dog]) -> None: + def func(animal: Cat | Dog) -> None: # OK: 'sleep' is defined for both Cat and Dog animal.sleep() - # Error: Item "Cat" of "Union[Cat, Dog]" has no attribute "follow_me" [union-attr] + # Error: Item "Cat" of "Cat | Dog" has no attribute "follow_me" [union-attr] animal.follow_me() You can often work around these errors by using ``assert isinstance(obj, ClassName)`` @@ -124,8 +122,6 @@ Example: .. code-block:: python - from typing import Sequence - def greet(name: str) -> None: print('hello', name) @@ -144,9 +140,7 @@ Example: .. code-block:: python - from typing import Optional - - def first(x: list[int]) -> Optional[int]: + def first(x: list[int]) -> int: return x[0] if x else 0 t = (5, 4) @@ -167,7 +161,7 @@ Example: .. code-block:: python - from typing import overload, Optional + from typing import overload @overload def inc_maybe(x: None) -> None: ... @@ -175,7 +169,7 @@ Example: @overload def inc_maybe(x: int) -> int: ... - def inc_maybe(x: Optional[int]) -> Optional[int]: + def inc_maybe(x: int | None) -> int | None: if x is None: return None else: @@ -210,11 +204,11 @@ This example incorrectly uses the function ``log`` as a type: for x in objs: f(x) -You can use :py:data:`~typing.Callable` as the type for callable objects: +You can use :py:class:`~collections.abc.Callable` as the type for callable objects: .. code-block:: python - from typing import Callable + from collections.abc import Callable # OK def log_all(objs: list[object], f: Callable[[object], None]) -> None: @@ -275,16 +269,14 @@ Example: .. code-block:: python - from typing import Optional, Union - class Base: def method(self, - arg: int) -> Optional[int]: + arg: int) -> int | None: ... class Derived(Base): def method(self, - arg: Union[int, str]) -> int: # OK + arg: int | str) -> int: # OK ... class DerivedBad(Base): @@ -434,15 +426,11 @@ Check type variable values [type-var] Mypy checks that value of a type variable is compatible with a value restriction or the upper bound type. -Example: +Example (Python 3.12 syntax): .. code-block:: python - from typing import TypeVar - - T1 = TypeVar('T1', int, float) - - def add(x: T1, y: T1) -> T1: + def add[T1: (int, float)](x: T1, y: T1) -> T1: return x + y add(4, 5.5) # OK @@ -783,27 +771,25 @@ Example: Safe handling of abstract type object types [type-abstract] ----------------------------------------------------------- -Mypy always allows instantiating (calling) type objects typed as ``Type[t]``, +Mypy always allows instantiating (calling) type objects typed as ``type[t]``, even if it is not known that ``t`` is non-abstract, since it is a common pattern to create functions that act as object factories (custom constructors). Therefore, to prevent issues described in the above section, when an abstract -type object is passed where ``Type[t]`` is expected, mypy will give an error. -Example: +type object is passed where ``type[t]`` is expected, mypy will give an error. +Example (Python 3.12 syntax): .. code-block:: python from abc import ABCMeta, abstractmethod - from typing import List, Type, TypeVar class Config(metaclass=ABCMeta): @abstractmethod def get_value(self, attr: str) -> str: ... - T = TypeVar("T") - def make_many(typ: Type[T], n: int) -> List[T]: + def make_many[T](typ: type[T], n: int) -> list[T]: return [typ() for _ in range(n)] # This will raise if typ is abstract - # Error: Only concrete class can be given where "Type[Config]" is expected [type-abstract] + # Error: Only concrete class can be given where "type[Config]" is expected [type-abstract] make_many(Config, 5) .. _code-safe-super: @@ -1149,6 +1135,34 @@ types you expect. See :ref:`overloading ` for more explanation. + +.. _code-overload-cannot-match: + +Check for overload signatures that cannot match [overload-cannot-match] +-------------------------------------------------------------------------- + +Warn if an ``@overload`` variant can never be matched, because an earlier +overload has a wider signature. For example, this can happen if the two +overloads accept the same parameters and each parameter on the first overload +has the same type or a wider type than the corresponding parameter on the second +overload. + +Example: + +.. code-block:: python + + from typing import overload, Union + + @overload + def process(response1: object, response2: object) -> object: + ... + @overload + def process(response1: int, response2: int) -> int: # E: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] + ... + + def process(response1: object, response2: object) -> object: + return response1 + response2 + .. _code-annotation-unchecked: Notify about an annotation in an unchecked function [annotation-unchecked] @@ -1184,7 +1198,7 @@ comment: .. code-block:: python - class MyClass + class MyClass: @special # type: ignore[prop-decorator] @property def magic(self) -> str: @@ -1203,6 +1217,30 @@ If the code being checked is not syntactically valid, mypy issues a syntax error. Most, but not all, syntax errors are *blocking errors*: they can't be ignored with a ``# type: ignore`` comment. +.. _code-typeddict-readonly-mutated: + +ReadOnly key of a TypedDict is mutated [typeddict-readonly-mutated] +------------------------------------------------------------------- + +Consider this example: + +.. code-block:: python + + from datetime import datetime + from typing import TypedDict + from typing_extensions import ReadOnly + + class User(TypedDict): + username: ReadOnly[str] + last_active: datetime + + user: User = {'username': 'foobar', 'last_active': datetime.now()} + user['last_active'] = datetime.now() # ok + user['username'] = 'other' # error: ReadOnly TypedDict key "key" TypedDict is mutated [typeddict-readonly-mutated] + +`PEP 705 `_ specifies +how ``ReadOnly`` special form works for ``TypedDict`` objects. + .. _code-misc: Miscellaneous checks [misc] diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index 2b765e412913d..df8b696745fc3 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -231,6 +231,44 @@ incorrect control flow or conditional checks that are accidentally always true o # Error: Statement is unreachable [unreachable] print('unreachable') +.. _code-deprecated: + +Check that imported or used feature is deprecated [deprecated] +-------------------------------------------------------------- + +If you use :option:`--enable-error-code deprecated `, +mypy generates an error if your code imports a deprecated feature explicitly with a +``from mod import depr`` statement or uses a deprecated feature imported otherwise or defined +locally. Features are considered deprecated when decorated with ``warnings.deprecated``, as +specified in `PEP 702 `_. +Use the :option:`--report-deprecated-as-note ` option to +turn all such errors into notes. + +.. note:: + + The ``warnings`` module provides the ``@deprecated`` decorator since Python 3.13. + To use it with older Python versions, import it from ``typing_extensions`` instead. + +Examples: + +.. code-block:: python + + # mypy: report-deprecated-as-error + + # Error: abc.abstractproperty is deprecated: Deprecated, use 'property' with 'abstractmethod' instead + from abc import abstractproperty + + from typing_extensions import deprecated + + @deprecated("use new_function") + def old_function() -> None: + print("I am old") + + # Error: __main__.old_function is deprecated: use new_function + old_function() + old_function() # type: ignore[deprecated] + + .. _code-redundant-expr: Check that expression is redundant [redundant-expr] @@ -270,7 +308,7 @@ example: # mypy: enable-error-code="possibly-undefined" - from typing import Iterable + from collections.abc import Iterable def test(values: Iterable[int], flag: bool) -> None: if flag: @@ -318,7 +356,7 @@ Example: .. code-block:: python - from typing import Iterable + from collections.abc import Iterable def transform(items: Iterable[int]) -> list[int]: # Error: "items" has type "Iterable[int]" which can always be true in boolean context. Consider using "Collection[int]" instead. [truthy-iterable] @@ -389,7 +427,7 @@ Example: # Are you missing an await? asyncio.create_task(f()) -You can assign the value to a temporary, otherwise unused to variable to +You can assign the value to a temporary, otherwise unused variable to silence the error: .. code-block:: python diff --git a/docs/source/extending_mypy.rst b/docs/source/extending_mypy.rst index bbbec2ad3880d..0df45ea22d336 100644 --- a/docs/source/extending_mypy.rst +++ b/docs/source/extending_mypy.rst @@ -179,7 +179,7 @@ mypy will call ``get_method_signature_hook("ctypes.Array.__setitem__")`` so that the plugin can mimic the :py:mod:`ctypes` auto-convert behavior. **get_attribute_hook()** overrides instance member field lookups and property -access (not assignments, and not method calls). This hook is only called for +access (not method calls). This hook is only called for fields which already exist on the class. *Exception:* if :py:meth:`__getattr__ ` or :py:meth:`__getattribute__ ` is a method on the class, the hook is called for all fields which do not refer to methods. @@ -245,4 +245,4 @@ Mypy ships ``mypy.plugins.proper_plugin`` plugin which can be useful for plugin authors, since it finds missing ``get_proper_type()`` calls, which is a pretty common mistake. -It is recommended to enable it is a part of your plugin's CI. +It is recommended to enable it as a part of your plugin's CI. diff --git a/docs/source/faq.rst b/docs/source/faq.rst index 195805382cd3b..b7f5e3759a7e8 100644 --- a/docs/source/faq.rst +++ b/docs/source/faq.rst @@ -102,8 +102,8 @@ Structural subtyping can be thought of as "static duck typing". Some argue that structural subtyping is better suited for languages with duck typing such as Python. Mypy however primarily uses nominal subtyping, leaving structural subtyping mostly opt-in (except for built-in protocols -such as :py:class:`~typing.Iterable` that always support structural subtyping). Here are some -reasons why: +such as :py:class:`~collections.abc.Iterable` that always support structural +subtyping). Here are some reasons why: 1. It is easy to generate short and informative error messages when using a nominal type system. This is especially important when @@ -140,13 +140,14 @@ How are mypy programs different from normal Python? Since you use a vanilla Python implementation to run mypy programs, mypy programs are also Python programs. The type checker may give warnings for some valid Python code, but the code is still always -runnable. Also, some Python features and syntax are still not +runnable. Also, a few Python features are still not supported by mypy, but this is gradually improving. The obvious difference is the availability of static type checking. The section :ref:`common_issues` mentions some modifications to Python code that may be required to make code type -check without errors. Also, your code must make attributes explicit. +check without errors. Also, your code must make defined +attributes explicit. Mypy supports modular, efficient type checking, and this seems to rule out type checking some language features, such as arbitrary diff --git a/docs/source/final_attrs.rst b/docs/source/final_attrs.rst index 297b97eca7873..81bfba650430c 100644 --- a/docs/source/final_attrs.rst +++ b/docs/source/final_attrs.rst @@ -25,8 +25,8 @@ Final names You can use the ``typing.Final`` qualifier to indicate that a name or attribute should not be reassigned, redefined, or -overridden. This is often useful for module and class level constants -as a way to prevent unintended modification. Mypy will prevent +overridden. This is often useful for module and class-level +constants to prevent unintended modification. Mypy will prevent further assignments to final names in type-checked code: .. code-block:: python @@ -70,7 +70,7 @@ You can use ``Final`` in one of these forms: ID: Final[int] = 1 - Here mypy will infer type ``int`` for ``ID``. + Here, mypy will infer type ``int`` for ``ID``. * You can omit the type: @@ -78,15 +78,15 @@ You can use ``Final`` in one of these forms: ID: Final = 1 - Here mypy will infer type ``Literal[1]`` for ``ID``. Note that unlike for - generic classes this is *not* the same as ``Final[Any]``. + Here, mypy will infer type ``Literal[1]`` for ``ID``. Note that unlike for + generic classes, this is *not* the same as ``Final[Any]``. -* In class bodies and stub files you can omit the right hand side and just write +* In class bodies and stub files, you can omit the right-hand side and just write ``ID: Final[int]``. * Finally, you can write ``self.id: Final = 1`` (also optionally with a type in square brackets). This is allowed *only* in - :py:meth:`__init__ ` methods, so that the final instance attribute is + :py:meth:`__init__ ` methods so the final instance attribute is assigned only once when an instance is created. Details of using ``Final`` @@ -129,7 +129,7 @@ the scope of a final declaration automatically depending on whether it was initialized in the class body or in :py:meth:`__init__ `. A final attribute can't be overridden by a subclass (even with another -explicit final declaration). Note however that a final attribute can +explicit final declaration). Note, however, that a final attribute can override a read-only property: .. code-block:: python @@ -176,12 +176,12 @@ overriding. You can use the ``typing.final`` decorator for this purpose: This ``@final`` decorator can be used with instance methods, class methods, static methods, and properties. -For overloaded methods you should add ``@final`` on the implementation +For overloaded methods, you should add ``@final`` on the implementation to make it final (or on the first overload in stubs): .. code-block:: python - from typing import Any, overload + from typing import final, overload class Base: @overload @@ -224,7 +224,7 @@ Here are some situations where using a final class may be useful: An abstract class that defines at least one abstract method or property and has ``@final`` decorator will generate an error from -mypy, since those attributes could never be implemented. +mypy since those attributes could never be implemented. .. code-block:: python diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 01ae7534ba936..4ba6d322417d0 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -2,7 +2,7 @@ Generics ======== This section explains how you can define your own generic classes that take -one or more type parameters, similar to built-in types such as ``list[X]``. +one or more type arguments, similar to built-in types such as ``list[T]``. User-defined generics are a moderately advanced feature and you can get far without ever using them -- feel free to skip this section and come back later. @@ -12,18 +12,48 @@ Defining generic classes ************************ The built-in collection classes are generic classes. Generic types -have one or more type parameters, which can be arbitrary types. For -example, ``dict[int, str]`` has the type parameters ``int`` and -``str``, and ``list[int]`` has a type parameter ``int``. +accept one or more type arguments within ``[...]``, which can be +arbitrary types. For example, the type ``dict[int, str]`` has the +type arguments ``int`` and ``str``, and ``list[int]`` has the type +argument ``int``. Programs can also define new generic classes. Here is a very simple -generic class that represents a stack: +generic class that represents a stack (using the syntax introduced in +Python 3.12): + +.. code-block:: python + + class Stack[T]: + def __init__(self) -> None: + # Create an empty list with items of type T + self.items: list[T] = [] + + def push(self, item: T) -> None: + self.items.append(item) + + def pop(self) -> T: + return self.items.pop() + + def empty(self) -> bool: + return not self.items + +There are two syntax variants for defining generic classes in Python. +Python 3.12 introduced a +`new dedicated syntax `_ +for defining generic classes (and also functions and type aliases, which +we will discuss later). The above example used the new syntax. Most examples are +given using both the new and the old (or legacy) syntax variants. +Unless mentioned otherwise, they work the same -- but the new syntax +is more readable and more convenient. + +Here is the same example using the old syntax (required for Python 3.11 +and earlier, but also supported on newer Python versions): .. code-block:: python from typing import TypeVar, Generic - T = TypeVar('T') + T = TypeVar('T') # Define type variable "T" class Stack(Generic[T]): def __init__(self) -> None: @@ -39,8 +69,16 @@ generic class that represents a stack: def empty(self) -> bool: return not self.items +.. note:: + + There are currently no plans to deprecate the legacy syntax. + You can freely mix code using the new and old syntax variants, + even within a single file (but *not* within a single class). + The ``Stack`` class can be used to represent a stack of any type: -``Stack[int]``, ``Stack[tuple[int, str]]``, etc. +``Stack[int]``, ``Stack[tuple[int, str]]``, etc. You can think of +``Stack[int]`` as referring to the definition of ``Stack`` above, +but with all instances of ``T`` replaced with ``int``. Using ``Stack`` is similar to built-in container types: @@ -50,19 +88,49 @@ Using ``Stack`` is similar to built-in container types: stack = Stack[int]() stack.push(2) stack.pop() - stack.push('x') # error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int" -Construction of instances of generic types is type checked: + # error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int" + stack.push('x') + + stack2: Stack[str] = Stack() + stack2.append('x') + +Construction of instances of generic types is type checked (Python 3.12 syntax): .. code-block:: python - class Box(Generic[T]): + class Box[T]: def __init__(self, content: T) -> None: self.content = content Box(1) # OK, inferred type is Box[int] Box[int](1) # Also OK - Box[int]('some string') # error: Argument 1 to "Box" has incompatible type "str"; expected "int" + + # error: Argument 1 to "Box" has incompatible type "str"; expected "int" + Box[int]('some string') + +Here is the definition of ``Box`` using the legacy syntax (Python 3.11 and earlier): + +.. code-block:: python + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class Box(Generic[T]): + def __init__(self, content: T) -> None: + self.content = content + +.. note:: + + Before moving on, let's clarify some terminology. + The name ``T`` in ``class Stack[T]`` or ``class Stack(Generic[T])`` + declares a *type parameter* ``T`` (of class ``Stack``). + ``T`` is also called a *type variable*, especially in a type annotation, + such as in the signature of ``push`` above. + When the type ``Stack[...]`` is used in a type annotation, the type + within square brackets is called a *type argument*. + This is similar to the distinction between function parameters and arguments. .. _generic-subclasses: @@ -70,7 +138,37 @@ Defining subclasses of generic classes ************************************** User-defined generic classes and generic classes defined in :py:mod:`typing` -can be used as a base class for another class (generic or non-generic). For example: +can be used as a base class for another class (generic or non-generic). For +example (Python 3.12 syntax): + +.. code-block:: python + + from typing import Mapping, Iterator + + # This is a generic subclass of Mapping + class MyMap[KT, VT](Mapping[KT, VT]): + def __getitem__(self, k: KT) -> VT: ... + def __iter__(self) -> Iterator[KT]: ... + def __len__(self) -> int: ... + + items: MyMap[str, int] # OK + + # This is a non-generic subclass of dict + class StrDict(dict[str, str]): + def __str__(self) -> str: + return f'StrDict({super().__str__()})' + + data: StrDict[int, int] # Error! StrDict is not generic + data2: StrDict # OK + + # This is a user-defined generic class + class Receiver[T]: + def accept(self, value: T) -> None: ... + + # This is a generic subclass of Receiver + class AdvancedReceiver[T](Receiver[T]): ... + +Here is the above example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -92,7 +190,6 @@ can be used as a base class for another class (generic or non-generic). For exam def __str__(self) -> str: return f'StrDict({super().__str__()})' - data: StrDict[int, int] # Error! StrDict is not generic data2: StrDict # OK @@ -105,25 +202,27 @@ can be used as a base class for another class (generic or non-generic). For exam .. note:: - You have to add an explicit :py:class:`~typing.Mapping` base class + You have to add an explicit :py:class:`~collections.abc.Mapping` base class if you want mypy to consider a user-defined class as a mapping (and - :py:class:`~typing.Sequence` for sequences, etc.). This is because mypy doesn't use - *structural subtyping* for these ABCs, unlike simpler protocols - like :py:class:`~typing.Iterable`, which use :ref:`structural subtyping `. + :py:class:`~collections.abc.Sequence` for sequences, etc.). This is because + mypy doesn't use *structural subtyping* for these ABCs, unlike simpler protocols + like :py:class:`~collections.abc.Iterable`, which use + :ref:`structural subtyping `. -:py:class:`Generic ` can be omitted from bases if there are +When using the legacy syntax, :py:class:`Generic ` can be omitted +from bases if there are other base classes that include type variables, such as ``Mapping[KT, VT]`` in the above example. If you include ``Generic[...]`` in bases, then it should list all type variables present in other bases (or more, -if needed). The order of type variables is defined by the following +if needed). The order of type parameters is defined by the following rules: -* If ``Generic[...]`` is present, then the order of variables is +* If ``Generic[...]`` is present, then the order of parameters is always determined by their order in ``Generic[...]``. -* If there are no ``Generic[...]`` in bases, then all type variables +* If there are no ``Generic[...]`` in bases, then all type parameters are collected in the lexicographic order (i.e. by first appearance). -For example: +Example: .. code-block:: python @@ -142,12 +241,26 @@ For example: x: First[int, str] # Here T is bound to int, S is bound to str y: Second[int, str, Any] # Here T is Any, S is int, and U is str +When using the Python 3.12 syntax, all type parameters must always be +explicitly defined immediately after the class name within ``[...]``, and the +``Generic[...]`` base class is never used. + .. _generic-functions: Generic functions ***************** -Type variables can be used to define generic functions: +Functions can also be generic, i.e. they can have type parameters (Python 3.12 syntax): + +.. code-block:: python + + from collections.abc import Sequence + + # A generic function! + def first[T](seq: Sequence[T]) -> T: + return seq[0] + +Here is the same example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -159,24 +272,25 @@ Type variables can be used to define generic functions: def first(seq: Sequence[T]) -> T: return seq[0] -As with generic classes, the type variable can be replaced with any -type. That means ``first`` can be used with any sequence type, and the -return type is derived from the sequence item type. For example: +As with generic classes, the type parameter ``T`` can be replaced with any +type. That means ``first`` can be passed an argument with any sequence type, +and the return type is derived from the sequence item type. Example: .. code-block:: python reveal_type(first([1, 2, 3])) # Revealed type is "builtins.int" - reveal_type(first(['a', 'b'])) # Revealed type is "builtins.str" + reveal_type(first(('a', 'b'))) # Revealed type is "builtins.str" -Note also that a single definition of a type variable (such as ``T`` -above) can be used in multiple generic functions or classes. In this -example we use the same type variable in two generic functions: +When using the legacy syntax, a single definition of a type variable +(such as ``T`` above) can be used in multiple generic functions or +classes. In this example we use the same type variable in two generic +functions to declarare type parameters: .. code-block:: python from typing import TypeVar, Sequence - T = TypeVar('T') # Declare type variable + T = TypeVar('T') # Define type variable def first(seq: Sequence[T]) -> T: return seq[0] @@ -184,20 +298,109 @@ example we use the same type variable in two generic functions: def last(seq: Sequence[T]) -> T: return seq[-1] +Since the Python 3.12 syntax is more concise, it doesn't need (or have) +an equivalent way of sharing type parameter definitions. + A variable cannot have a type variable in its type unless the type variable is bound in a containing generic class or function. +When calling a generic function, you can't explicitly pass the values of +type parameters as type arguments. The values of type parameters are always +inferred by mypy. This is not valid: + +.. code-block:: python + + first[int]([1, 2]) # Error: can't use [...] with generic function + +If you really need this, you can define a generic class with a ``__call__`` +method. + +.. _type-variable-upper-bound: + +Type variables with upper bounds +******************************** + +A type variable can also be restricted to having values that are +subtypes of a specific type. This type is called the upper bound of +the type variable, and it is specified using ``T: `` when using the +Python 3.12 syntax. In the definition of a generic function or a generic +class that uses such a type variable ``T``, the type represented by ``T`` +is assumed to be a subtype of its upper bound, so you can use methods +of the upper bound on values of type ``T`` (Python 3.12 syntax): + +.. code-block:: python + + from typing import SupportsAbs + + def max_by_abs[T: SupportsAbs[float]](*xs: T) -> T: + # We can use abs(), because T is a subtype of SupportsAbs[float]. + return max(xs, key=abs) + +An upper bound can also be specified with the ``bound=...`` keyword +argument to :py:class:`~typing.TypeVar`. +Here is the example using the legacy syntax (Python 3.11 and earlier): + +.. code-block:: python + + from typing import TypeVar, SupportsAbs + + T = TypeVar('T', bound=SupportsAbs[float]) + + def max_by_abs(*xs: T) -> T: + return max(xs, key=abs) + +In a call to such a function, the type ``T`` must be replaced by a +type that is a subtype of its upper bound. Continuing the example +above: + +.. code-block:: python + + max_by_abs(-3.5, 2) # Okay, has type 'float' + max_by_abs(5+6j, 7) # Okay, has type 'complex' + max_by_abs('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float] + +Type parameters of generic classes may also have upper bounds, which +restrict the valid values for the type parameter in the same way. + .. _generic-methods-and-generic-self: Generic methods and generic self ******************************** -You can also define generic methods — just use a type variable in the -method signature that is different from class type variables. In -particular, the ``self`` argument may also be generic, allowing a +You can also define generic methods. In +particular, the ``self`` parameter may also be generic, allowing a method to return the most precise type known at the point of access. In this way, for example, you can type check a chain of setter -methods: +methods (Python 3.12 syntax): + +.. code-block:: python + + class Shape: + def set_scale[T: Shape](self: T, scale: float) -> T: + self.scale = scale + return self + + class Circle(Shape): + def set_radius(self, r: float) -> 'Circle': + self.radius = r + return self + + class Square(Shape): + def set_width(self, w: float) -> 'Square': + self.width = w + return self + + circle: Circle = Circle().set_scale(0.5).set_radius(2.7) + square: Square = Square().set_scale(0.5).set_width(3.2) + +Without using generic ``self``, the last two lines could not be type +checked properly, since the return type of ``set_scale`` would be +``Shape``, which doesn't define ``set_radius`` or ``set_width``. + +When using the legacy syntax, just use a type variable in the +method signature that is different from class type parameters (if any +are defined). Here is the above example using the legacy +syntax (3.11 and earlier): .. code-block:: python @@ -223,24 +426,40 @@ methods: circle: Circle = Circle().set_scale(0.5).set_radius(2.7) square: Square = Square().set_scale(0.5).set_width(3.2) -Without using generic ``self``, the last two lines could not be type -checked properly, since the return type of ``set_scale`` would be -``Shape``, which doesn't define ``set_radius`` or ``set_width``. +Other uses include factory methods, such as copy and deserialization methods. +For class methods, you can also define generic ``cls``, using ``type[T]`` +or :py:class:`Type[T] ` (Python 3.12 syntax): + +.. code-block:: python + + class Friend: + other: "Friend | None" = None -Other uses are factory methods, such as copy and deserialization. -For class methods, you can also define generic ``cls``, using :py:class:`Type[T] `: + @classmethod + def make_pair[T: Friend](cls: type[T]) -> tuple[T, T]: + a, b = cls(), cls() + a.other = b + b.other = a + return a, b + + class SuperFriend(Friend): + pass + + a, b = SuperFriend.make_pair() + +Here is the same example using the legacy syntax (3.11 and earlier): .. code-block:: python - from typing import TypeVar, Type + from typing import TypeVar T = TypeVar('T', bound='Friend') class Friend: - other: "Friend" = None + other: "Friend | None" = None @classmethod - def make_pair(cls: Type[T]) -> tuple[T, T]: + def make_pair(cls: type[T]) -> tuple[T, T]: a, b = cls(), cls() a.other = b b.other = a @@ -260,18 +479,15 @@ or a deserialization method returns the actual type of self. Therefore you may need to silence mypy inside these methods (but not at the call site), possibly by making use of the ``Any`` type or a ``# type: ignore`` comment. -Note that mypy lets you use generic self types in certain unsafe ways +Mypy lets you use generic self types in certain unsafe ways in order to support common idioms. For example, using a generic -self type in an argument type is accepted even though it's unsafe: +self type in an argument type is accepted even though it's unsafe (Python 3.12 +syntax): .. code-block:: python - from typing import TypeVar - - T = TypeVar("T") - class Base: - def compare(self: T, other: T) -> bool: + def compare[T: Base](self: T, other: T) -> bool: return False class Sub(Base): @@ -280,7 +496,7 @@ self type in an argument type is accepted even though it's unsafe: # This is unsafe (see below) but allowed because it's # a common pattern and rarely causes issues in practice. - def compare(self, other: Sub) -> bool: + def compare(self, other: 'Sub') -> bool: return self.x > other.x b: Base = Sub(42) @@ -293,12 +509,12 @@ Automatic self types using typing.Self Since the patterns described above are quite common, mypy supports a simpler syntax, introduced in :pep:`673`, to make them easier to use. -Instead of defining a type variable and using an explicit annotation +Instead of introducing a type parameter and using an explicit annotation for ``self``, you can import the special type ``typing.Self`` that is -automatically transformed into a type variable with the current class -as the upper bound, and you don't need an annotation for ``self`` (or -``cls`` in class methods). The example from the previous section can -be made simpler by using ``Self``: +automatically transformed into a method-level type parameter with the +current class as the upper bound, and you don't need an annotation for +``self`` (or ``cls`` in class methods). The example from the previous +section can be made simpler by using ``Self``: .. code-block:: python @@ -319,7 +535,7 @@ be made simpler by using ``Self``: a, b = SuperFriend.make_pair() -This is more compact than using explicit type variables. Also, you can +This is more compact than using explicit type parameters. Also, you can use ``Self`` in attribute annotations in addition to methods. .. note:: @@ -354,10 +570,10 @@ Let us illustrate this by few simple examples: class Triangle(Shape): ... class Square(Shape): ... -* Most immutable containers, such as :py:class:`~typing.Sequence` and - :py:class:`~typing.FrozenSet` are covariant. :py:data:`~typing.Union` is - also covariant in all variables: ``Union[Triangle, int]`` is - a subtype of ``Union[Shape, int]``. +* Most immutable container types, such as :py:class:`~collections.abc.Sequence` + and :py:class:`~frozenset` are covariant. Union types are + also covariant in all union items: ``Triangle | int`` is + a subtype of ``Shape | int``. .. code-block:: python @@ -367,7 +583,7 @@ Let us illustrate this by few simple examples: triangles: Sequence[Triangle] count_lines(triangles) # OK - def foo(triangle: Triangle, num: int): + def foo(triangle: Triangle, num: int) -> None: shape_or_number: Union[Shape, int] # a Triangle is a Shape, and a Shape is a valid Union[Shape, int] shape_or_number = triangle @@ -375,7 +591,7 @@ Let us illustrate this by few simple examples: Covariance should feel relatively intuitive, but contravariance and invariance can be harder to reason about. -* :py:data:`~typing.Callable` is an example of type that behaves contravariant +* :py:class:`~collections.abc.Callable` is an example of type that behaves contravariant in types of arguments. That is, ``Callable[[Shape], int]`` is a subtype of ``Callable[[Triangle], int]``, despite ``Shape`` being a supertype of ``Triangle``. To understand this, consider: @@ -400,8 +616,8 @@ Let us illustrate this by few simple examples: triangle. If we give it a callable that can calculate the area of an arbitrary shape (not just triangles), everything still works. -* :py:class:`~typing.List` is an invariant generic type. Naively, one would think - that it is covariant, like :py:class:`~typing.Sequence` above, but consider this code: +* ``list`` is an invariant generic type. Naively, one would think + that it is covariant, like :py:class:`~collections.abc.Sequence` above, but consider this code: .. code-block:: python @@ -416,107 +632,104 @@ Let us illustrate this by few simple examples: add_one(my_circles) # This may appear safe, but... my_circles[-1].rotate() # ...this will fail, since my_circles[0] is now a Shape, not a Circle - Another example of invariant type is :py:class:`~typing.Dict`. Most mutable containers + Another example of invariant type is ``dict``. Most mutable containers are invariant. -By default, mypy assumes that all user-defined generics are invariant. -To declare a given generic class as covariant or contravariant use -type variables defined with special keyword arguments ``covariant`` or -``contravariant``. For example: +When using the Python 3.12 syntax for generics, mypy will automatically +infer the most flexible variance for each class type variable. Here +``Box`` will be inferred as covariant: .. code-block:: python - from typing import Generic, TypeVar - - T_co = TypeVar('T_co', covariant=True) - - class Box(Generic[T_co]): # this type is declared covariant - def __init__(self, content: T_co) -> None: + class Box[T]: # this type is implicitly covariant + def __init__(self, content: T) -> None: self._content = content - def get_content(self) -> T_co: + def get_content(self) -> T: return self._content - def look_into(box: Box[Animal]): ... + def look_into(box: Box[Shape]): ... - my_box = Box(Cat()) + my_box = Box(Square()) look_into(my_box) # OK, but mypy would complain here for an invariant type -.. _type-variable-upper-bound: - -Type variables with upper bounds -******************************** - -A type variable can also be restricted to having values that are -subtypes of a specific type. This type is called the upper bound of -the type variable, and is specified with the ``bound=...`` keyword -argument to :py:class:`~typing.TypeVar`. +Here the underscore prefix for ``_content`` is significant. Without an +underscore prefix, the class would be invariant, as the attribute would +be understood as a public, mutable attribute (a single underscore prefix +has no special significance for mypy in most other contexts). By declaring +the attribute as ``Final``, the class could still be made covariant: .. code-block:: python - from typing import TypeVar, SupportsAbs + from typing import Final - T = TypeVar('T', bound=SupportsAbs[float]) + class Box[T]: # this type is implicitly covariant + def __init__(self, content: T) -> None: + self.content: Final = content + + def get_content(self) -> T: + return self.content -In the definition of a generic function that uses such a type variable -``T``, the type represented by ``T`` is assumed to be a subtype of -its upper bound, so the function can use methods of the upper bound on -values of type ``T``. +When using the legacy syntax, mypy assumes that all user-defined generics +are invariant by default. To declare a given generic class as covariant or +contravariant, use type variables defined with special keyword arguments +``covariant`` or ``contravariant``. For example (Python 3.11 or earlier): .. code-block:: python - def largest_in_absolute_value(*xs: T) -> T: - return max(xs, key=abs) # Okay, because T is a subtype of SupportsAbs[float]. + from typing import Generic, TypeVar -In a call to such a function, the type ``T`` must be replaced by a -type that is a subtype of its upper bound. Continuing the example -above: + T_co = TypeVar('T_co', covariant=True) -.. code-block:: python + class Box(Generic[T_co]): # this type is declared covariant + def __init__(self, content: T_co) -> None: + self._content = content - largest_in_absolute_value(-3.5, 2) # Okay, has type float. - largest_in_absolute_value(5+6j, 7) # Okay, has type complex. - largest_in_absolute_value('a', 'b') # Error: 'str' is not a subtype of SupportsAbs[float]. + def get_content(self) -> T_co: + return self._content -Type parameters of generic classes may also have upper bounds, which -restrict the valid values for the type parameter in the same way. + def look_into(box: Box[Shape]): ... + + my_box = Box(Square()) + look_into(my_box) # OK, but mypy would complain here for an invariant type .. _type-variable-value-restriction: Type variables with value restriction ************************************* -By default, a type variable can be replaced with any type. However, sometimes +By default, a type variable can be replaced with any type -- or any type that +is a subtype of the upper bound, which defaults to ``object``. However, sometimes it's useful to have a type variable that can only have some specific types as its value. A typical example is a type variable that can only have values -``str`` and ``bytes``: +``str`` and ``bytes``. This lets us define a function that can concatenate +two strings or bytes objects, but it can't be called with other argument +types (Python 3.12 syntax): .. code-block:: python - from typing import TypeVar + def concat[S: (str, bytes)](x: S, y: S) -> S: + return x + y - AnyStr = TypeVar('AnyStr', str, bytes) + concat('a', 'b') # Okay + concat(b'a', b'b') # Okay + concat(1, 2) # Error! -This is actually such a common type variable that :py:data:`~typing.AnyStr` is -defined in :py:mod:`typing` and we don't need to define it ourselves. -We can use :py:data:`~typing.AnyStr` to define a function that can concatenate -two strings or bytes objects, but it can't be called with other -argument types: +The same thing is also possibly using the legacy syntax (Python 3.11 or earlier): .. code-block:: python - from typing import AnyStr + from typing import TypeVar + + AnyStr = TypeVar('AnyStr', str, bytes) def concat(x: AnyStr, y: AnyStr) -> AnyStr: return x + y - concat('a', 'b') # Okay - concat(b'a', b'b') # Okay - concat(1, 2) # Error! - -Importantly, this is different from a union type, since combinations -of ``str`` and ``bytes`` are not accepted: +No matter which syntax you use, such a type variable is called a type variable +with a value restriction. Importantly, this is different from a union type, +since combinations of ``str`` and ``bytes`` are not accepted: .. code-block:: python @@ -524,11 +737,11 @@ of ``str`` and ``bytes`` are not accepted: In this case, this is exactly what we want, since it's not possible to concatenate a string and a bytes object! If we tried to use -``Union``, the type checker would complain about this possibility: +a union type, the type checker would complain about this possibility: .. code-block:: python - def union_concat(x: Union[str, bytes], y: Union[str, bytes]) -> Union[str, bytes]: + def union_concat(x: str | bytes, y: str | bytes) -> str | bytes: return x + y # Error: can't concatenate str and bytes Another interesting special case is calling ``concat()`` with a @@ -545,24 +758,28 @@ You may expect that the type of ``ss`` is ``S``, but the type is actually ``str``: a subtype gets promoted to one of the valid values for the type variable, which in this case is ``str``. -This is thus -subtly different from *bounded quantification* in languages such as -Java, where the return type would be ``S``. The way mypy implements -this is correct for ``concat``, since ``concat`` actually returns a -``str`` instance in the above example: +This is thus subtly different from using ``str | bytes`` as an upper bound, +where the return type would be ``S`` (see :ref:`type-variable-upper-bound`). +Using a value restriction is correct for ``concat``, since ``concat`` +actually returns a ``str`` instance in the above example: .. code-block:: python >>> print(type(ss)) -You can also use a :py:class:`~typing.TypeVar` with a restricted set of possible -values when defining a generic class. For example, mypy uses the type -:py:class:`Pattern[AnyStr] ` for the return value of :py:func:`re.compile`, -since regular expressions can be based on a string or a bytes pattern. +You can also use type variables with a restricted set of possible +values when defining a generic class. For example, the type +:py:class:`Pattern[S] ` is used for the return +value of :py:func:`re.compile`, where ``S`` can be either ``str`` +or ``bytes``. Regular expressions can be based on a string or a +bytes pattern. + +A type variable may not have both a value restriction and an upper bound. -A type variable may not have both a value restriction (see -:ref:`type-variable-upper-bound`) and an upper bound. +Note that you may come across :py:data:`~typing.AnyStr` imported from +:py:mod:`typing`. This feature is now deprecated, but it means the same +as our definition of ``AnyStr`` above. .. _declaring-decorators: @@ -571,11 +788,12 @@ Declaring decorators Decorators are typically functions that take a function as an argument and return another function. Describing this behaviour in terms of types can -be a little tricky; we'll show how you can use ``TypeVar`` and a special +be a little tricky; we'll show how you can use type variables and a special kind of type variable called a *parameter specification* to do so. Suppose we have the following decorator, not type annotated yet, -that preserves the original function's signature and merely prints the decorated function's name: +that preserves the original function's signature and merely prints the decorated +function's name: .. code-block:: python @@ -585,7 +803,7 @@ that preserves the original function's signature and merely prints the decorated return func(*args, **kwds) return wrapper -and we use it to decorate function ``add_forty_two``: +We can use it to decorate function ``add_forty_two``: .. code-block:: python @@ -611,11 +829,34 @@ Note that class decorators are handled differently than function decorators in mypy: decorating a class does not erase its type, even if the decorator has incomplete type annotations. -Here's how one could annotate the decorator: +Here's how one could annotate the decorator (Python 3.12 syntax): .. code-block:: python - from typing import Any, Callable, TypeVar, cast + from collections.abc import Callable + from typing import Any, cast + + # A decorator that preserves the signature. + def printing_decorator[F: Callable[..., Any]](func: F) -> F: + def wrapper(*args, **kwds): + print("Calling", func) + return func(*args, **kwds) + return cast(F, wrapper) + + @printing_decorator + def add_forty_two(value: int) -> int: + return value + 42 + + a = add_forty_two(3) + reveal_type(a) # Revealed type is "builtins.int" + add_forty_two('x') # Argument 1 to "add_forty_two" has incompatible type "str"; expected "int" + +Here is the example using the legacy syntax (Python 3.11 and earlier): + +.. code-block:: python + + from collections.abc import Callable + from typing import Any, TypeVar, cast F = TypeVar('F', bound=Callable[..., Any]) @@ -636,19 +877,33 @@ Here's how one could annotate the decorator: This still has some shortcomings. First, we need to use the unsafe :py:func:`~typing.cast` to convince mypy that ``wrapper()`` has the same -signature as ``func``. See :ref:`casts `. +signature as ``func`` (see :ref:`casts `). Second, the ``wrapper()`` function is not tightly type checked, although wrapper functions are typically small enough that this is not a big problem. This is also the reason for the :py:func:`~typing.cast` call in the ``return`` statement in ``printing_decorator()``. -However, we can use a parameter specification (:py:class:`~typing.ParamSpec`), -for a more faithful type annotation: +However, we can use a parameter specification, introduced using ``**P``, +for a more faithful type annotation (Python 3.12 syntax): + +.. code-block:: python + + from collections.abc import Callable + + def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[P, T]: + def wrapper(*args: P.args, **kwds: P.kwargs) -> T: + print("Calling", func) + return func(*args, **kwds) + return wrapper + +The same is possible using the legacy syntax with :py:class:`~typing.ParamSpec` +(Python 3.11 and earlier): .. code-block:: python - from typing import Callable, TypeVar + from collections.abc import Callable + from typing import TypeVar from typing_extensions import ParamSpec P = ParamSpec('P') @@ -661,18 +916,14 @@ for a more faithful type annotation: return wrapper Parameter specifications also allow you to describe decorators that -alter the signature of the input function: +alter the signature of the input function (Python 3.12 syntax): .. code-block:: python - from typing import Callable, TypeVar - from typing_extensions import ParamSpec - - P = ParamSpec('P') - T = TypeVar('T') + from collections.abc import Callable - # We reuse 'P' in the return type, but replace 'T' with 'str' - def stringify(func: Callable[P, T]) -> Callable[P, str]: + # We reuse 'P' in the return type, but replace 'T' with 'str' + def stringify[**P, T](func: Callable[P, T]) -> Callable[P, str]: def wrapper(*args: P.args, **kwds: P.kwargs) -> str: return str(func(*args, **kwds)) return wrapper @@ -685,17 +936,31 @@ alter the signature of the input function: reveal_type(a) # Revealed type is "builtins.str" add_forty_two('x') # error: Argument 1 to "add_forty_two" has incompatible type "str"; expected "int" -Or insert an argument: +Here is the above example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - from typing import Callable, TypeVar - from typing_extensions import Concatenate, ParamSpec + from collections.abc import Callable + from typing import TypeVar + from typing_extensions import ParamSpec - P = ParamSpec('P') - T = TypeVar('T') + P = ParamSpec('P') + T = TypeVar('T') - def printing_decorator(func: Callable[P, T]) -> Callable[Concatenate[str, P], T]: + # We reuse 'P' in the return type, but replace 'T' with 'str' + def stringify(func: Callable[P, T]) -> Callable[P, str]: + def wrapper(*args: P.args, **kwds: P.kwargs) -> str: + return str(func(*args, **kwds)) + return wrapper + +You can also insert an argument in a decorator (Python 3.12 syntax): + +.. code-block:: python + + from collections.abc import Callable + from typing import Concatenate + + def printing_decorator[**P, T](func: Callable[P, T]) -> Callable[Concatenate[str, P], T]: def wrapper(msg: str, /, *args: P.args, **kwds: P.kwargs) -> T: print("Calling", func, "with", msg) return func(*args, **kwds) @@ -707,17 +972,54 @@ Or insert an argument: a = add_forty_two('three', 3) +Here is the same function using the legacy syntax (Python 3.11 and earlier): + +.. code-block:: python + + from collections.abc import Callable + from typing import TypeVar + from typing_extensions import Concatenate, ParamSpec + + P = ParamSpec('P') + T = TypeVar('T') + + def printing_decorator(func: Callable[P, T]) -> Callable[Concatenate[str, P], T]: + def wrapper(msg: str, /, *args: P.args, **kwds: P.kwargs) -> T: + print("Calling", func, "with", msg) + return func(*args, **kwds) + return wrapper + .. _decorator-factories: Decorator factories ------------------- Functions that take arguments and return a decorator (also called second-order decorators), are -similarly supported via generics: +similarly supported via generics (Python 3.12 syntax): + +.. code-block:: python + + from colletions.abc import Callable + from typing import Any + + def route[F: Callable[..., Any]](url: str) -> Callable[[F], F]: + ... + + @route(url='/') + def index(request: Any) -> str: + return 'Hello world' + +Note that mypy infers that ``F`` is used to make the ``Callable`` return value +of ``route`` generic, instead of making ``route`` itself generic, since ``F`` is +only used in the return type. Python has no explicit syntax to mark that ``F`` +is only bound in the return value. + +Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - from typing import Any, Callable, TypeVar + from collections.abc import Callable + from typing import Any, TypeVar F = TypeVar('F', bound=Callable[..., Any]) @@ -729,23 +1031,22 @@ similarly supported via generics: return 'Hello world' Sometimes the same decorator supports both bare calls and calls with arguments. This can be -achieved by combining with :py:func:`@overload `: +achieved by combining with :py:func:`@overload ` (Python 3.12 syntax): .. code-block:: python - from typing import Any, Callable, Optional, TypeVar, overload - - F = TypeVar('F', bound=Callable[..., Any]) + from collections.abc import Callable + from typing import Any, overload # Bare decorator usage @overload - def atomic(__func: F) -> F: ... + def atomic[F: Callable[..., Any]](func: F, /) -> F: ... # Decorator with arguments @overload - def atomic(*, savepoint: bool = True) -> Callable[[F], F]: ... + def atomic[F: Callable[..., Any]](*, savepoint: bool = True) -> Callable[[F], F]: ... # Implementation - def atomic(__func: Optional[Callable[..., Any]] = None, *, savepoint: bool = True): + def atomic(func: Callable[..., Any] | None = None, /, *, savepoint: bool = True): def decorator(func: Callable[..., Any]): ... # Code goes here if __func is not None: @@ -760,21 +1061,41 @@ achieved by combining with :py:func:`@overload `: @atomic(savepoint=False) def func2() -> None: ... +Here is the decorator from the example using the legacy syntax +(Python 3.11 and earlier): + +.. code-block:: python + + from collections.abc import Callable + from typing import Any, Optional, TypeVar, overload + + F = TypeVar('F', bound=Callable[..., Any]) + + # Bare decorator usage + @overload + def atomic(func: F, /) -> F: ... + # Decorator with arguments + @overload + def atomic(*, savepoint: bool = True) -> Callable[[F], F]: ... + + # Implementation + def atomic(func: Optional[Callable[..., Any]] = None, /, *, savepoint: bool = True): + ... # Same as above + Generic protocols ***************** Mypy supports generic protocols (see also :ref:`protocol-types`). Several :ref:`predefined protocols ` are generic, such as -:py:class:`Iterable[T] `, and you can define additional generic protocols. Generic -protocols mostly follow the normal rules for generic classes. Example: +:py:class:`Iterable[T] `, and you can define additional +generic protocols. Generic protocols mostly follow the normal rules for +generic classes. Example (Python 3.12 syntax): .. code-block:: python - from typing import Protocol, TypeVar + from typing import Protocol - T = TypeVar('T') - - class Box(Protocol[T]): + class Box[T](Protocol): content: T def do_stuff(one: Box[str], other: Box[bytes]) -> None: @@ -794,15 +1115,29 @@ protocols mostly follow the normal rules for generic classes. Example: y: Box[int] = ... x = y # Error -- Box is invariant +Here is the definition of ``Box`` from the above example using the legacy +syntax (Python 3.11 and earlier): + +.. code-block:: python + + from typing import Protocol, TypeVar + + T = TypeVar('T') + + class Box(Protocol[T]): + content: T + Note that ``class ClassName(Protocol[T])`` is allowed as a shorthand for -``class ClassName(Protocol, Generic[T])``, as per :pep:`PEP 544: Generic protocols <544#generic-protocols>`, +``class ClassName(Protocol, Generic[T])`` when using the legacy syntax, +as per :pep:`PEP 544: Generic protocols <544#generic-protocols>`. +This form is only valid when using the legacy syntax. -The main difference between generic protocols and ordinary generic -classes is that mypy checks that the declared variances of generic -type variables in a protocol match how they are used in the protocol -definition. The protocol in this example is rejected, since the type -variable ``T`` is used covariantly as a return type, but the type -variable is invariant: +When using the legacy syntax, there is an important difference between +generic protocols and ordinary generic classes: mypy checks that the +declared variances of generic type variables in a protocol match how +they are used in the protocol definition. The protocol in this example +is rejected, since the type variable ``T`` is used covariantly as +a return type, but the type variable is invariant: .. code-block:: python @@ -830,13 +1165,11 @@ This example correctly uses a covariant type variable: See :ref:`variance-of-generics` for more about variance. -Generic protocols can also be recursive. Example: +Generic protocols can also be recursive. Example (Python 3.12 synta): .. code-block:: python - T = TypeVar('T') - - class Linked(Protocol[T]): + class Linked[T](Protocol): val: T def next(self) -> 'Linked[T]': ... @@ -849,17 +1182,63 @@ Generic protocols can also be recursive. Example: result = last(L()) reveal_type(result) # Revealed type is "builtins.int" +Here is the definition of ``Linked`` using the legacy syntax +(Python 3.11 and earlier): + +.. code-block:: python + + from typing import TypeVar + + T = TypeVar('T') + + class Linked(Protocol[T]): + val: T + def next(self) -> 'Linked[T]': ... + .. _generic-type-aliases: Generic type aliases ******************** -Type aliases can be generic. In this case they can be used in two ways: -Subscripted aliases are equivalent to original types with substituted type -variables, so the number of type arguments must match the number of free type variables -in the generic type alias. Unsubscripted aliases are treated as original types with free -variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases -<484#type-aliases>`): +Type aliases can be generic. In this case they can be used in two ways. +First, subscripted aliases are equivalent to original types with substituted type +variables. Second, unsubscripted aliases are treated as original types with type +parameters replaced with ``Any``. + +The ``type`` statement introduced in Python 3.12 is used to define generic +type aliases (it also supports non-generic type aliases): + +.. code-block:: python + + from collections.abc import Callable, Iterable + + type TInt[S] = tuple[int, S] + type UInt[S] = S | int + type CBack[S] = Callable[..., S] + + def response(query: str) -> UInt[str]: # Same as str | int + ... + def activate[S](cb: CBack[S]) -> S: # Same as Callable[..., S] + ... + table_entry: TInt # Same as tuple[int, Any] + + type Vec[T: (int, float, complex)] = Iterable[tuple[T, T]] + + def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: + return sum(x*y for x, y in v) + + def dilate[T: (int, float, complex)](v: Vec[T], scale: T) -> Vec[T]: + return ((x * scale, y * scale) for x, y in v) + + v1: Vec[int] = [] # Same as Iterable[tuple[int, int]] + v2: Vec = [] # Same as Iterable[tuple[Any, Any]] + v3: Vec[int, int] = [] # Error: Invalid alias, too many type arguments! + +There is also a legacy syntax that relies on ``TypeVar``. +Here the number of type arguments must match the number of free type variables +in the generic type alias definition. A type variables is free if it's not +a type parameter of a surrounding class or function. Example (following +:pep:`PEP 484: Type aliases <484#type-aliases>`, Python 3.11 and earlier): .. code-block:: python @@ -867,7 +1246,7 @@ variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases S = TypeVar('S') - TInt = tuple[int, S] + TInt = tuple[int, S] # 1 type parameter, since only S is free UInt = Union[S, int] CBack = Callable[..., S] @@ -894,7 +1273,36 @@ variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases Type aliases can be imported from modules just like other names. An alias can also target another alias, although building complex chains of aliases is not recommended -- this impedes code readability, thus -defeating the purpose of using aliases. Example: +defeating the purpose of using aliases. Example (Python 3.12 syntax): + +.. code-block:: python + + from example1 import AliasType + from example2 import Vec + + # AliasType and Vec are type aliases (Vec as defined above) + + def fun() -> AliasType: + ... + + type OIntVec = Vec[int] | None + +Type aliases defined using the ``type`` statement are not valid as +base classes, and they can't be used to construct instances: + +.. code-block:: python + + from example1 import AliasType + from example2 import Vec + + # AliasType and Vec are type aliases (Vec as defined above) + + class NewVec[T](Vec[T]): # Error: not valid as base class + ... + + x = AliasType() # Error: can't be used to create instances + +Here are examples using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -907,18 +1315,49 @@ defeating the purpose of using aliases. Example: def fun() -> AliasType: ... + OIntVec = Optional[Vec[int]] + T = TypeVar('T') + # Old-style type aliases can be used as base classes and you can + # construct instances using them + class NewVec(Vec[T]): ... + x = AliasType() + for i, j in NewVec[int](): ... - OIntVec = Optional[Vec[int]] +Using type variable bounds or value restriction in generic aliases has +the same effect as in generic classes and functions. + + +Differences between the new and old syntax +****************************************** -Using type variable bounds or values in generic aliases has the same effect -as in generic classes/functions. +There are a few notable differences between the new (Python 3.12 and later) +and the old syntax for generic classes, functions and type aliases, beyond +the obvious syntactic differences: + + * Type variables defined using the old syntax create definitions at runtime + in the surrounding namespace, whereas the type variables defined using the + new syntax are only defined within the class, function or type variable + that uses them. + * Type variable definitions can be shared when using the old syntax, but + the new syntax doesn't support this. + * When using the new syntax, the variance of class type variables is always + inferred. + * Type aliases defined using the new syntax can contain forward references + and recursive references without using string literal escaping. The + same is true for the bounds and constraints of type variables. + * The new syntax lets you define a generic alias where the definition doesn't + contain a reference to a type parameter. This is occasionally useful, at + least when conditionally defining type aliases. + * Type aliases defined using the new syntax can't be used as base classes + and can't be used to construct instances, unlike aliases defined using the + old syntax. Generic class internals @@ -926,7 +1365,20 @@ Generic class internals You may wonder what happens at runtime when you index a generic class. Indexing returns a *generic alias* to the original class that returns instances -of the original class on instantiation: +of the original class on instantiation (Python 3.12 syntax): + +.. code-block:: python + + >>> class Stack[T]: ... + >>> Stack + __main__.Stack + >>> Stack[int] + __main__.Stack[int] + >>> instance = Stack[int]() + >>> instance.__class__ + __main__.Stack + +Here is the same example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python @@ -945,10 +1397,17 @@ Generic aliases can be instantiated or subclassed, similar to real classes, but the above examples illustrate that type variables are erased at runtime. Generic ``Stack`` instances are just ordinary Python objects, and they have no extra runtime overhead or magic due -to being generic, other than a metaclass that overloads the indexing -operator. +to being generic, other than the ``Generic`` base class that overloads +the indexing operator using ``__class_getitem__``. ``typing.Generic`` +is included as an implicit base class even when using the new syntax: + +.. code-block:: python + + >>> class Stack[T]: ... + >>> Stack.mro() + [, , ] -Note that in Python 3.8 and lower, the built-in types +Note that in Python 3.8 and earlier, the built-in types :py:class:`list`, :py:class:`dict` and others do not support indexing. This is why we have the aliases :py:class:`~typing.List`, :py:class:`~typing.Dict` and so on in the :py:mod:`typing` @@ -959,16 +1418,18 @@ class in more recent versions of Python: .. code-block:: python >>> # Only relevant for Python 3.8 and below - >>> # For Python 3.9 onwards, prefer `list[int]` syntax + >>> # If using Python 3.9 or newer, prefer the 'list[int]' syntax >>> from typing import List >>> List[int] typing.List[int] Note that the generic aliases in ``typing`` don't support constructing -instances: +instances, unlike the corresponding built-in classes: .. code-block:: python + >>> list[int]() + [] >>> from typing import List >>> List[int]() Traceback (most recent call last): diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 049d7af003b5d..28a4481e502eb 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -186,19 +186,23 @@ For example, a ``RuntimeError`` instance can be passed to a function that is ann as taking an ``Exception``. As another example, suppose you want to write a function that can accept *either* -ints or strings, but no other types. You can express this using the -:py:data:`~typing.Union` type. For example, ``int`` is a subtype of ``Union[int, str]``: +ints or strings, but no other types. You can express this using a +union type. For example, ``int`` is a subtype of ``int | str``: .. code-block:: python - from typing import Union - - def normalize_id(user_id: Union[int, str]) -> str: + def normalize_id(user_id: int | str) -> str: if isinstance(user_id, int): return f'user-{100_000 + user_id}' else: return user_id +.. note:: + + If using Python 3.9 or earlier, use ``typing.Union[int, str]`` instead of + ``int | str``, or use ``from __future__ import annotations`` at the top of + the file (see :ref:`runtime_troubles`). + The :py:mod:`typing` module contains many other useful types. For a quick overview, look through the :ref:`mypy cheatsheet `. @@ -210,12 +214,12 @@ generic types or your own type aliases), look through the .. note:: When adding types, the convention is to import types - using the form ``from typing import Union`` (as opposed to doing + using the form ``from typing import `` (as opposed to doing just ``import typing`` or ``import typing as t`` or ``from typing import *``). For brevity, we often omit imports from :py:mod:`typing` or :py:mod:`collections.abc` in code examples, but mypy will give an error if you use types such as - :py:class:`~typing.Iterable` without first importing them. + :py:class:`~collections.abc.Iterable` without first importing them. .. note:: diff --git a/docs/source/index.rst b/docs/source/index.rst index c9dc6bc1f8c99..de3286d58aced 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -103,6 +103,7 @@ Contents error_code_list2 additional_features faq + changelog .. toctree:: :hidden: diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index d07eb40753f33..54693cddf953b 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -136,7 +136,7 @@ purpose. Example: .. note:: Usually it's a better idea to use ``Sequence[T]`` instead of ``tuple[T, ...]``, as - :py:class:`~typing.Sequence` is also compatible with lists and other non-tuple sequences. + :py:class:`~collections.abc.Sequence` is also compatible with lists and other non-tuple sequences. .. note:: @@ -155,7 +155,7 @@ and returns ``Rt`` is ``Callable[[A1, ..., An], Rt]``. Example: .. code-block:: python - from typing import Callable + from collections.abc import Callable def twice(i: int, next: Callable[[int], int]) -> int: return next(next(i)) @@ -165,6 +165,11 @@ and returns ``Rt`` is ``Callable[[A1, ..., An], Rt]``. Example: print(twice(3, add)) # 5 +.. note:: + + Import :py:data:`Callable[...] ` from ``typing`` instead + of ``collections.abc`` if you use Python 3.8 or earlier. + You can only have positional arguments, and only ones without default values, in callable types. These cover the vast majority of uses of callable types, but sometimes this isn't quite enough. Mypy recognizes @@ -178,7 +183,7 @@ Any)`` function signature. Example: .. code-block:: python - from typing import Callable + from collections.abc import Callable def arbitrary_call(f: Callable[..., int]) -> int: return f('x') + f(y=2) # OK @@ -205,7 +210,7 @@ Callables can also be used against type objects, matching their .. code-block:: python - from typing import Callable + from collections.abc import Callable class C: def __init__(self, app: str) -> None: @@ -223,6 +228,7 @@ instance of ``C`` or the type of ``C`` itself. This also works with .. _union-types: +.. _alternative_union_syntax: Union types *********** @@ -231,8 +237,8 @@ Python functions often accept values of two or more different types. You can use :ref:`overloading ` to represent this, but union types are often more convenient. -Use the ``Union[T1, ..., Tn]`` type constructor to construct a union -type. For example, if an argument has type ``Union[int, str]``, both +Use ``T1 | ... | Tn`` to construct a union +type. For example, if an argument has type ``int | str``, both integers and strings are valid argument values. You can use an :py:func:`isinstance` check to narrow down a union type to a @@ -240,9 +246,7 @@ more specific type: .. code-block:: python - from typing import Union - - def f(x: Union[int, str]) -> None: + def f(x: int | str) -> None: x + 1 # Error: str + int is not valid if isinstance(x, int): # Here type of x is int. @@ -264,20 +268,38 @@ more specific type: since the caller may have to use :py:func:`isinstance` before doing anything interesting with the value. +Python 3.9 and older only partially support this syntax. Instead, you can +use the legacy ``Union[T1, ..., Tn]`` type constructor. Example: + +.. code-block:: python + + from typing import Union + + def f(x: Union[int, str]) -> None: + ... + +It is also possible to use the new syntax with versions of Python where it +isn't supported by the runtime with some limitations, if you use +``from __future__ import annotations`` (see :ref:`runtime_troubles`): + +.. code-block:: python + + from __future__ import annotations + + def f(x: int | str) -> None: # OK on Python 3.7 and later + ... + .. _strict_optional: Optional types and the None type ******************************** -You can use the :py:data:`~typing.Optional` type modifier to define a type variant -that allows ``None``, such as ``Optional[int]`` (``Optional[X]`` is -the preferred shorthand for ``Union[X, None]``): +You can use ``T | None`` to define a type variant that allows ``None`` values, +such as ``int | None``. This is called an *optional type*: .. code-block:: python - from typing import Optional - - def strlen(s: str) -> Optional[int]: + def strlen(s: str) -> int | None: if not s: return None # OK return len(s) @@ -287,12 +309,23 @@ the preferred shorthand for ``Union[X, None]``): return None # Error: None not compatible with int return len(s) -Most operations will not be allowed on unguarded ``None`` or :py:data:`~typing.Optional` -values: +To support Python 3.9 and earlier, you can use the :py:data:`~typing.Optional` +type modifier instead, such as ``Optional[int]`` (``Optional[X]`` is +the preferred shorthand for ``Union[X, None]``): .. code-block:: python - def my_inc(x: Optional[int]) -> int: + from typing import Optional + + def strlen(s: str) -> Optional[int]: + ... + +Most operations will not be allowed on unguarded ``None`` or *optional* values +(values with an optional type): + +.. code-block:: python + + def my_inc(x: int | None) -> int: return x + 1 # Error: Cannot add None and int Instead, an explicit ``None`` check is required. Mypy has @@ -302,7 +335,7 @@ recognizes ``is None`` checks: .. code-block:: python - def my_inc(x: Optional[int]) -> int: + def my_inc(x: int | None) -> int: if x is None: return 0 else: @@ -318,7 +351,7 @@ Other supported checks for guarding against a ``None`` value include .. code-block:: python - def concat(x: Optional[str], y: Optional[str]) -> Optional[str]: + def concat(x: str | None, y: str | None) -> str | None: if x is not None and y is not None: # Both x and y are not None here return x + y @@ -335,7 +368,7 @@ will complain about the possible ``None`` value. You can use .. code-block:: python class Resource: - path: Optional[str] = None + path: str | None = None def initialize(self, path: str) -> None: self.path = path @@ -358,7 +391,7 @@ This is why you need to annotate an attribute in cases like the class .. code-block:: python class Resource: - path: Optional[str] = None + path: str | None = None ... This also works for attributes defined within methods: @@ -367,10 +400,11 @@ This also works for attributes defined within methods: class Counter: def __init__(self) -> None: - self.count: Optional[int] = None + self.count: int | None = None -This is not a problem when using variable annotations, since no initial -value is needed: +Often it's easier to not use any initial value for an attribute. +This way you don't need to use an optional type and can avoid ``assert ... is not None`` +checks. No initial value is needed if you annotate an attribute in the class body: .. code-block:: python @@ -385,13 +419,13 @@ the right thing without an annotation: .. code-block:: python def f(i: int) -> None: - n = None # Inferred type Optional[int] because of the assignment below + n = None # Inferred type 'int | None' because of the assignment below if i > 0: n = i ... Sometimes you may get the error "Cannot determine type of ". In this -case you should add an explicit ``Optional[...]`` annotation (or type comment). +case you should add an explicit ``... | None`` annotation. .. note:: @@ -409,44 +443,31 @@ case you should add an explicit ``Optional[...]`` annotation (or type comment). .. note:: - ``Optional[...]`` *does not* mean a function argument with a default value. - It simply means that ``None`` is a valid value for the argument. This is - a common confusion because ``None`` is a common default value for arguments. - -.. _alternative_union_syntax: - -X | Y syntax for Unions ------------------------ - -:pep:`604` introduced an alternative way for spelling union types. In Python -3.10 and later, you can write ``Union[int, str]`` as ``int | str``. It is -possible to use this syntax in versions of Python where it isn't supported by -the runtime with some limitations (see :ref:`runtime_troubles`). - -.. code-block:: python - - t1: int | str # equivalent to Union[int, str] - - t2: int | None # equivalent to Optional[int] + The type ``Optional[T]`` *does not* mean a function parameter with a default value. + It simply means that ``None`` is a valid argument value. This is + a common confusion because ``None`` is a common default value for parameters, + and parameters with default values are sometimes called *optional* parameters + (or arguments). .. _type-aliases: Type aliases ************ -In certain situations, type names may end up being long and painful to type: +In certain situations, type names may end up being long and painful to type, +especially if they are used frequently: .. code-block:: python - def f() -> Union[list[dict[tuple[int, str], set[int]]], tuple[str, list[str]]]: + def f() -> list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]]: ... When cases like this arise, you can define a type alias by simply -assigning the type to a variable: +assigning the type to a variable (this is an *implicit type alias*): .. code-block:: python - AliasType = Union[list[dict[tuple[int, str], set[int]]], tuple[str, list[str]]] + AliasType = list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]] # Now we can use AliasType in place of the full name: @@ -459,8 +480,18 @@ assigning the type to a variable: another type -- it's equivalent to the target type except for :ref:`generic aliases `. -Since Mypy 0.930 you can also use *explicit type aliases*, which were -introduced in :pep:`613`. +Python 3.12 introduced the ``type`` statement for defining *explicit type aliases*. +Explicit type aliases are unambiguous and can also improve readability by +making the intent clear: + +.. code-block:: python + + type AliasType = list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]] + + # Now we can use AliasType in place of the full name: + + def f() -> AliasType: + ... There can be confusion about exactly when an assignment defines an implicit type alias -- for example, when the alias contains forward references, invalid types, or violates some other @@ -469,14 +500,23 @@ distinction between an unannotated variable and a type alias is implicit, ambiguous or incorrect type alias declarations default to defining a normal variable instead of a type alias. -Explicit type aliases are unambiguous and can also improve readability by -making the intent clear: +Aliases defined using the ``type`` statement have these properties, which +distinguish them from implicit type aliases: + +* The definition may contain forward references without having to use string + literal escaping, since it is evaluated lazily. +* The alias can be used in type annotations, type arguments, and casts, but + it can't be used in contexts which require a class object. For example, it's + not valid as a base class and it can't be used to construct instances. + +There is also use an older syntax for defining explicit type aliases, which was +introduced in Python 3.10 (:pep:`613`): .. code-block:: python from typing import TypeAlias # "from typing_extensions" in Python 3.9 and earlier - AliasType: TypeAlias = Union[list[dict[tuple[int, str], set[int]]], tuple[str, list[str]]] + AliasType: TypeAlias = list[dict[tuple[int, str], set[int]]] | tuple[str, list[str]] .. _named-tuples: @@ -604,14 +644,21 @@ doesn't see that the ``buyer`` variable has type ``ProUser``: buyer.pay() # Rejected, not a method on User However, using the ``type[C]`` syntax and a type variable with an upper bound (see -:ref:`type-variable-upper-bound`) we can do better: +:ref:`type-variable-upper-bound`) we can do better (Python 3.12 syntax): + +.. code-block:: python + + def new_user[U: User](user_class: type[U]) -> U: + # Same implementation as before + +Here is the example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python U = TypeVar('U', bound=User) def new_user(user_class: type[U]) -> U: - # Same implementation as before + # Same implementation as before Now mypy will infer the correct type of the result when we call ``new_user()`` with a specific subclass of ``User``: diff --git a/docs/source/literal_types.rst b/docs/source/literal_types.rst index 283bf7f9dba1a..877ab5de90877 100644 --- a/docs/source/literal_types.rst +++ b/docs/source/literal_types.rst @@ -70,7 +70,7 @@ complex types involving literals a little more convenient. Literal types may also contain ``None``. Mypy will treat ``Literal[None]`` as being equivalent to just ``None``. This means that ``Literal[4, None]``, -``Union[Literal[4], None]``, and ``Optional[Literal[4]]`` are all equivalent. +``Literal[4] | None``, and ``Optional[Literal[4]]`` are all equivalent. Literals may also contain aliases to other literal types. For example, the following program is legal: @@ -264,19 +264,15 @@ use the same technique with regular objects, tuples, or namedtuples. Similarly, tags do not need to be specifically str Literals: they can be any type you can normally narrow within ``if`` statements and the like. For example, you could have your tags be int or Enum Literals or even regular classes you narrow -using ``isinstance()``: +using ``isinstance()`` (Python 3.12 syntax): .. code-block:: python - from typing import Generic, TypeVar, Union - - T = TypeVar('T') - - class Wrapper(Generic[T]): + class Wrapper[T]: def __init__(self, inner: T) -> None: self.inner = inner - def process(w: Union[Wrapper[int], Wrapper[str]]) -> None: + def process(w: Wrapper[int] | Wrapper[str]) -> None: # Doing `if isinstance(w, Wrapper[int])` does not work: isinstance requires # that the second argument always be an *erased* type, with no generics. # This is because generics are a typing-only concept and do not exist at diff --git a/docs/source/metaclasses.rst b/docs/source/metaclasses.rst index 396d7dbb42cc0..a3ee25f160546 100644 --- a/docs/source/metaclasses.rst +++ b/docs/source/metaclasses.rst @@ -34,13 +34,12 @@ Mypy supports the lookup of attributes in the metaclass: .. code-block:: python - from typing import Type, TypeVar, ClassVar - T = TypeVar('T') + from typing import ClassVar, Self class M(type): count: ClassVar[int] = 0 - def make(cls: Type[T]) -> T: + def make(cls) -> Self: M.count += 1 return cls() @@ -56,6 +55,9 @@ Mypy supports the lookup of attributes in the metaclass: b: B = B.make() # metaclasses are inherited print(B.count + " objects were created") # Error: Unsupported operand types for + ("int" and "str") +.. note:: + In Python 3.10 and earlier, ``Self`` is available in ``typing_extensions``. + .. _limitations: Gotchas and limitations of metaclass support diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index cb3ef64b39a74..cbf40d5dcaa51 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -116,7 +116,7 @@ implicitly casting from ``UserId`` where ``int`` is expected. Examples: :py:class:`~typing.NewType` accepts exactly two arguments. The first argument must be a string literal containing the name of the new type and must equal the name of the variable to which the new type is assigned. The second argument must be a properly subclassable class, i.e., -not a type construct like :py:data:`~typing.Union`, etc. +not a type construct like a :ref:`union type `, etc. The callable returned by :py:class:`~typing.NewType` accepts only one argument; this is equivalent to supporting only one constructor accepting an instance of the base class (see above). @@ -179,7 +179,7 @@ Function overloading ******************** Sometimes the arguments and types in a function depend on each other -in ways that can't be captured with a :py:data:`~typing.Union`. For example, suppose +in ways that can't be captured with a :ref:`union types `. For example, suppose we want to write a function that can accept x-y coordinates. If we pass in just a single x-y coordinate, we return a ``ClickEvent`` object. However, if we pass in two x-y coordinates, we return a ``DragEvent`` object. @@ -188,12 +188,10 @@ Our first attempt at writing this function might look like this: .. code-block:: python - from typing import Union, Optional - def mouse_event(x1: int, y1: int, - x2: Optional[int] = None, - y2: Optional[int] = None) -> Union[ClickEvent, DragEvent]: + x2: int | None = None, + y2: int | None = None) -> ClickEvent | DragEvent: if x2 is None and y2 is None: return ClickEvent(x1, y1) elif x2 is not None and y2 is not None: @@ -213,7 +211,7 @@ to more accurately describe the function's behavior: .. code-block:: python - from typing import Union, overload + from typing import overload # Overload *variants* for 'mouse_event'. # These variants give extra information to the type checker. @@ -236,8 +234,8 @@ to more accurately describe the function's behavior: def mouse_event(x1: int, y1: int, - x2: Optional[int] = None, - y2: Optional[int] = None) -> Union[ClickEvent, DragEvent]: + x2: int | None = None, + y2: int | None = None) -> ClickEvent | DragEvent: if x2 is None and y2 is None: return ClickEvent(x1, y1) elif x2 is not None and y2 is not None: @@ -253,14 +251,37 @@ calls like ``mouse_event(5, 25, 2)``. As another example, suppose we want to write a custom container class that implements the :py:meth:`__getitem__ ` method (``[]`` bracket indexing). If this method receives an integer we return a single item. If it receives a -``slice``, we return a :py:class:`~typing.Sequence` of items. +``slice``, we return a :py:class:`~collections.abc.Sequence` of items. We can precisely encode this relationship between the argument and the -return type by using overloads like so: +return type by using overloads like so (Python 3.12 syntax): + +.. code-block:: python + + from collections.abc import Sequence + from typing import overload + + class MyList[T](Sequence[T]): + @overload + def __getitem__(self, index: int) -> T: ... + + @overload + def __getitem__(self, index: slice) -> Sequence[T]: ... + + def __getitem__(self, index: int | slice) -> T | Sequence[T]: + if isinstance(index, int): + # Return a T here + elif isinstance(index, slice): + # Return a sequence of Ts here + else: + raise TypeError(...) + +Here is the same example using the legacy syntax (Python 3.11 and earlier): .. code-block:: python - from typing import Sequence, TypeVar, Union, overload + from collections.abc import Sequence + from typing import TypeVar, overload T = TypeVar('T') @@ -271,7 +292,7 @@ return type by using overloads like so: @overload def __getitem__(self, index: slice) -> Sequence[T]: ... - def __getitem__(self, index: Union[int, slice]) -> Union[T, Sequence[T]]: + def __getitem__(self, index: int | slice) -> T | Sequence[T]: if isinstance(index, int): # Return a T here elif isinstance(index, slice): @@ -389,9 +410,9 @@ matching variant returns: .. code-block:: python - some_list: Union[list[int], list[str]] + some_list: list[int] | list[str] - # output3 is of type 'Union[float, str]' + # output3 is of type 'float | str' output3 = summarize(some_list) .. note:: @@ -418,7 +439,7 @@ types: .. code-block:: python - from typing import overload, Union + from typing import overload class Expression: # ...snip... @@ -469,7 +490,7 @@ the following unsafe overload definition: .. code-block:: python - from typing import overload, Union + from typing import overload @overload def unsafe_func(x: int) -> int: ... @@ -477,7 +498,7 @@ the following unsafe overload definition: @overload def unsafe_func(x: object) -> str: ... - def unsafe_func(x: object) -> Union[int, str]: + def unsafe_func(x: object) -> int | str: if isinstance(x, int): return 42 else: @@ -546,8 +567,8 @@ Type checking the implementation The body of an implementation is type-checked against the type hints provided on the implementation. For example, in the ``MyList`` example up above, the code in the body is checked with -argument list ``index: Union[int, slice]`` and a return type of -``Union[T, Sequence[T]]``. If there are no annotations on the +argument list ``index: int | slice`` and a return type of +``T | Sequence[T]``. If there are no annotations on the implementation, then the body is not type checked. If you want to force mypy to check the body anyways, use the :option:`--check-untyped-defs ` flag (:ref:`more details here `). @@ -555,10 +576,10 @@ flag (:ref:`more details here `). The variants must also also be compatible with the implementation type hints. In the ``MyList`` example, mypy will check that the parameter type ``int`` and the return type ``T`` are compatible with -``Union[int, slice]`` and ``Union[T, Sequence]`` for the +``int | slice`` and ``T | Sequence`` for the first variant. For the second variant it verifies the parameter type ``slice`` and the return type ``Sequence[T]`` are compatible -with ``Union[int, slice]`` and ``Union[T, Sequence]``. +with ``int | slice`` and ``T | Sequence``. .. note:: @@ -697,14 +718,13 @@ Restricted methods in generic classes ------------------------------------- In generic classes some methods may be allowed to be called only -for certain values of type arguments: +for certain values of type arguments (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T') - - class Tag(Generic[T]): + class Tag[T]: item: T + def uppercase_item(self: Tag[str]) -> str: return self.item.upper() @@ -714,18 +734,18 @@ for certain values of type arguments: ts.uppercase_item() # This is OK This pattern also allows matching on nested types in situations where the type -argument is itself generic: +argument is itself generic (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T', covariant=True) - S = TypeVar('S') + from collections.abc import Sequence - class Storage(Generic[T]): + class Storage[T]: def __init__(self, content: T) -> None: - self.content = content - def first_chunk(self: Storage[Sequence[S]]) -> S: - return self.content[0] + self._content = content + + def first_chunk[S](self: Storage[Sequence[S]]) -> S: + return self._content[0] page: Storage[list[str]] page.first_chunk() # OK, type is "str" @@ -734,13 +754,14 @@ argument is itself generic: # "first_chunk" with type "Callable[[Storage[Sequence[S]]], S]" Finally, one can use overloads on self-type to express precise types of -some tricky methods: +some tricky methods (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T') + from collections.abc import Callable + from typing import overload - class Tag(Generic[T]): + class Tag[T]: @overload def export(self: Tag[str]) -> str: ... @overload @@ -799,23 +820,22 @@ Precise typing of alternative constructors ------------------------------------------ Some classes may define alternative constructors. If these -classes are generic, self-type allows giving them precise signatures: +classes are generic, self-type allows giving them precise +signatures (Python 3.12 syntax): .. code-block:: python - T = TypeVar('T') - - class Base(Generic[T]): - Q = TypeVar('Q', bound='Base[T]') + from typing import Self + class Base[T]: def __init__(self, item: T) -> None: self.item = item @classmethod - def make_pair(cls: Type[Q], item: T) -> tuple[Q, Q]: + def make_pair(cls, item: T) -> tuple[Self, Self]: return cls(item), cls(item) - class Sub(Base[T]): + class Sub[T](Base[T]): ... pair = Sub.make_pair('yes') # Type is "tuple[Sub[str], Sub[str]]" @@ -854,8 +874,8 @@ expect to get back when ``await``-ing the coroutine. The result of calling an ``async def`` function *without awaiting* will automatically be inferred to be a value of type -:py:class:`Coroutine[Any, Any, T] `, which is a subtype of -:py:class:`Awaitable[T] `: +:py:class:`Coroutine[Any, Any, T] `, which is a subtype of +:py:class:`Awaitable[T] `: .. code-block:: python @@ -868,11 +888,12 @@ Asynchronous iterators ---------------------- If you have an asynchronous iterator, you can use the -:py:class:`~typing.AsyncIterator` type in your annotations: +:py:class:`~collections.abc.AsyncIterator` type in your annotations: .. code-block:: python - from typing import Optional, AsyncIterator + from collections.abc import AsyncIterator + from typing import Optional import asyncio class arange: @@ -905,7 +926,8 @@ async iterators: .. code-block:: python - from typing import AsyncGenerator, Optional + from collections.abc import AsyncGenerator + from typing import Optional import asyncio # Could also type this as returning AsyncIterator[int] @@ -922,7 +944,7 @@ One common confusion is that the presence of a ``yield`` statement in an .. code-block:: python - from typing import AsyncIterator + from collections.abc import AsyncIterator async def arange(stop: int) -> AsyncIterator[int]: # When called, arange gives you an async iterator @@ -948,7 +970,8 @@ This can sometimes come up when trying to define base classes, Protocols or over .. code-block:: python - from typing import AsyncIterator, Protocol, overload + from collections.abc import AsyncIterator + from typing import Protocol, overload class LauncherIncorrect(Protocol): # Because launch does not have yield, this has type diff --git a/docs/source/protocols.rst b/docs/source/protocols.rst index 7315628676917..ed8d94f62ef15 100644 --- a/docs/source/protocols.rst +++ b/docs/source/protocols.rst @@ -27,18 +27,21 @@ of protocols and structural subtyping in Python. Predefined protocols ******************** -The :py:mod:`typing` module defines various protocol classes that correspond -to common Python protocols, such as :py:class:`Iterable[T] `. If a class +The :py:mod:`collections.abc`, :py:mod:`typing` and other stdlib modules define +various protocol classes that correspond to common Python protocols, such as +:py:class:`Iterable[T] `. If a class defines a suitable :py:meth:`__iter__ ` method, mypy understands that it -implements the iterable protocol and is compatible with :py:class:`Iterable[T] `. +implements the iterable protocol and is compatible with :py:class:`Iterable[T] `. For example, ``IntList`` below is iterable, over ``int`` values: .. code-block:: python - from typing import Iterator, Iterable, Optional + from __future__ import annotations + + from collections.abc import Iterator, Iterable class IntList: - def __init__(self, value: int, next: Optional['IntList']) -> None: + def __init__(self, value: int, next: IntList | None) -> None: self.value = value self.next = next @@ -56,9 +59,18 @@ For example, ``IntList`` below is iterable, over ``int`` values: print_numbered(x) # OK print_numbered([4, 5]) # Also OK -:ref:`predefined_protocols_reference` lists all protocols defined in -:py:mod:`typing` and the signatures of the corresponding methods you need to define -to implement each protocol. +:ref:`predefined_protocols_reference` lists various protocols defined in +:py:mod:`collections.abc` and :py:mod:`typing` and the signatures of the corresponding methods +you need to define to implement each protocol. + +.. note:: + ``typing`` also contains deprecated aliases to protocols and ABCs defined in + :py:mod:`collections.abc`, such as :py:class:`Iterable[T] `. + These are only necessary in Python 3.8 and earlier, since the protocols in + ``collections.abc`` didn't yet support subscripting (``[]``) in Python 3.8, + but the aliases in ``typing`` have always supported + subscripting. In Python 3.9 and later, the aliases in ``typing`` don't provide + any extra functionality. Simple user-defined protocols ***************************** @@ -68,7 +80,8 @@ class: .. code-block:: python - from typing import Iterable, Protocol + from collections.abc import Iterable + from typing import Protocol class SupportsClose(Protocol): # Empty method body (explicit '...') @@ -225,22 +238,24 @@ such as trees and linked lists: .. code-block:: python - from typing import TypeVar, Optional, Protocol + from __future__ import annotations + + from typing import Protocol class TreeLike(Protocol): value: int @property - def left(self) -> Optional['TreeLike']: ... + def left(self) -> TreeLike | None: ... @property - def right(self) -> Optional['TreeLike']: ... + def right(self) -> TreeLike | None: ... class SimpleTree: def __init__(self, value: int) -> None: self.value = value - self.left: Optional['SimpleTree'] = None - self.right: Optional['SimpleTree'] = None + self.left: SimpleTree | None = None + self.right: SimpleTree | None = None root: TreeLike = SimpleTree(0) # OK @@ -290,42 +305,46 @@ Callback protocols ****************** Protocols can be used to define flexible callback types that are hard -(or even impossible) to express using the :py:data:`Callable[...] ` syntax, such as variadic, -overloaded, and complex generic callbacks. They are defined with a special :py:meth:`__call__ ` -member: +(or even impossible) to express using the +:py:class:`Callable[...] ` syntax, +such as variadic, overloaded, and complex generic callbacks. They are defined with a +special :py:meth:`__call__ ` member: .. code-block:: python - from typing import Optional, Iterable, Protocol + from collections.abc import Iterable + from typing import Optional, Protocol class Combiner(Protocol): - def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: ... + def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... def batch_proc(data: Iterable[bytes], cb_results: Combiner) -> bytes: for item in data: ... - def good_cb(*vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: + def good_cb(*vals: bytes, maxlen: int | None = None) -> list[bytes]: ... - def bad_cb(*vals: bytes, maxitems: Optional[int]) -> list[bytes]: + def bad_cb(*vals: bytes, maxitems: int | None) -> list[bytes]: ... batch_proc([], good_cb) # OK batch_proc([], bad_cb) # Error! Argument 2 has incompatible type because of # different name and kind in the callback -Callback protocols and :py:data:`~typing.Callable` types can be used mostly interchangeably. -Argument names in :py:meth:`__call__ ` methods must be identical, unless -a double underscore prefix is used. For example: +Callback protocols and :py:class:`~collections.abc.Callable` types can be used mostly interchangeably. +Parameter names in :py:meth:`__call__ ` methods must be identical, unless +the parameters are positional-only. Example (using the legacy syntax for generic functions): .. code-block:: python - from typing import Callable, Protocol, TypeVar + from collections.abc import Callable + from typing import Protocol, TypeVar T = TypeVar('T') class Copy(Protocol): - def __call__(self, __origin: T) -> T: ... + # '/' marks the end of positional-only parameters + def __call__(self, origin: T, /) -> T: ... copy_a: Callable[[T], T] copy_b: Copy @@ -344,8 +363,8 @@ Iteration protocols The iteration protocols are useful in many contexts. For example, they allow iteration of objects in for loops. -Iterable[T] ------------ +collections.abc.Iterable[T] +--------------------------- The :ref:`example above ` has a simple implementation of an :py:meth:`__iter__ ` method. @@ -354,17 +373,17 @@ The :ref:`example above ` has a simple implementation of a def __iter__(self) -> Iterator[T] -See also :py:class:`~typing.Iterable`. +See also :py:class:`~collections.abc.Iterable`. -Iterator[T] ------------ +collections.abc.Iterator[T] +--------------------------- .. code-block:: python def __next__(self) -> T def __iter__(self) -> Iterator[T] -See also :py:class:`~typing.Iterator`. +See also :py:class:`~collections.abc.Iterator`. Collection protocols .................... @@ -373,8 +392,8 @@ Many of these are implemented by built-in container types such as :py:class:`list` and :py:class:`dict`, and these are also useful for user-defined collection objects. -Sized ------ +collections.abc.Sized +--------------------- This is a type for objects that support :py:func:`len(x) `. @@ -382,10 +401,10 @@ This is a type for objects that support :py:func:`len(x) `. def __len__(self) -> int -See also :py:class:`~typing.Sized`. +See also :py:class:`~collections.abc.Sized`. -Container[T] ------------- +collections.abc.Container[T] +---------------------------- This is a type for objects that support the ``in`` operator. @@ -393,10 +412,10 @@ This is a type for objects that support the ``in`` operator. def __contains__(self, x: object) -> bool -See also :py:class:`~typing.Container`. +See also :py:class:`~collections.abc.Container`. -Collection[T] -------------- +collections.abc.Collection[T] +----------------------------- .. code-block:: python @@ -404,7 +423,7 @@ Collection[T] def __iter__(self) -> Iterator[T] def __contains__(self, x: object) -> bool -See also :py:class:`~typing.Collection`. +See also :py:class:`~collections.abc.Collection`. One-off protocols ................. @@ -412,8 +431,8 @@ One-off protocols These protocols are typically only useful with a single standard library function or class. -Reversible[T] -------------- +collections.abc.Reversible[T] +----------------------------- This is a type for objects that support :py:func:`reversed(x) `. @@ -421,10 +440,10 @@ This is a type for objects that support :py:func:`reversed(x) `. def __reversed__(self) -> Iterator[T] -See also :py:class:`~typing.Reversible`. +See also :py:class:`~collections.abc.Reversible`. -SupportsAbs[T] --------------- +typing.SupportsAbs[T] +--------------------- This is a type for objects that support :py:func:`abs(x) `. ``T`` is the type of value returned by :py:func:`abs(x) `. @@ -435,8 +454,8 @@ value returned by :py:func:`abs(x) `. See also :py:class:`~typing.SupportsAbs`. -SupportsBytes -------------- +typing.SupportsBytes +-------------------- This is a type for objects that support :py:class:`bytes(x) `. @@ -448,8 +467,8 @@ See also :py:class:`~typing.SupportsBytes`. .. _supports-int-etc: -SupportsComplex ---------------- +typing.SupportsComplex +---------------------- This is a type for objects that support :py:class:`complex(x) `. Note that no arithmetic operations are supported. @@ -460,8 +479,8 @@ are supported. See also :py:class:`~typing.SupportsComplex`. -SupportsFloat -------------- +typing.SupportsFloat +-------------------- This is a type for objects that support :py:class:`float(x) `. Note that no arithmetic operations are supported. @@ -472,8 +491,8 @@ are supported. See also :py:class:`~typing.SupportsFloat`. -SupportsInt ------------ +typing.SupportsInt +------------------ This is a type for objects that support :py:class:`int(x) `. Note that no arithmetic operations are supported. @@ -484,8 +503,8 @@ are supported. See also :py:class:`~typing.SupportsInt`. -SupportsRound[T] ----------------- +typing.SupportsRound[T] +----------------------- This is a type for objects that support :py:func:`round(x) `. @@ -501,33 +520,33 @@ Async protocols These protocols can be useful in async code. See :ref:`async-and-await` for more information. -Awaitable[T] ------------- +collections.abc.Awaitable[T] +---------------------------- .. code-block:: python def __await__(self) -> Generator[Any, None, T] -See also :py:class:`~typing.Awaitable`. +See also :py:class:`~collections.abc.Awaitable`. -AsyncIterable[T] ----------------- +collections.abc.AsyncIterable[T] +-------------------------------- .. code-block:: python def __aiter__(self) -> AsyncIterator[T] -See also :py:class:`~typing.AsyncIterable`. +See also :py:class:`~collections.abc.AsyncIterable`. -AsyncIterator[T] ----------------- +collections.abc.AsyncIterator[T] +-------------------------------- .. code-block:: python def __anext__(self) -> Awaitable[T] def __aiter__(self) -> AsyncIterator[T] -See also :py:class:`~typing.AsyncIterator`. +See also :py:class:`~collections.abc.AsyncIterator`. Context manager protocols ......................... @@ -536,28 +555,28 @@ There are two protocols for context managers -- one for regular context managers and one for async ones. These allow defining objects that can be used in ``with`` and ``async with`` statements. -ContextManager[T] ------------------ +contextlib.AbstractContextManager[T] +------------------------------------ .. code-block:: python def __enter__(self) -> T def __exit__(self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType]) -> Optional[bool] + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> bool | None -See also :py:class:`~typing.ContextManager`. +See also :py:class:`~contextlib.AbstractContextManager`. -AsyncContextManager[T] ----------------------- +contextlib.AbstractAsyncContextManager[T] +----------------------------------------- .. code-block:: python def __aenter__(self) -> Awaitable[T] def __aexit__(self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType]) -> Awaitable[Optional[bool]] + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None) -> Awaitable[bool | None] -See also :py:class:`~typing.AsyncContextManager`. +See also :py:class:`~contextlib.AbstractAsyncContextManager`. diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 42474ae94c48c..ff042b395e997 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -277,6 +277,25 @@ If you are getting this error, try to obtain type hints for the library you're u to the library -- see our documentation on creating :ref:`PEP 561 compliant packages `. +4. Force mypy to analyze the library as best as it can (as if the library provided + a ``py.typed`` file), despite it likely missing any type annotations. In general, + the quality of type checking will be poor and mypy may have issues when + analyzing code not designed to be type checked. + + You can do this via setting the + :option:`--follow-untyped-imports ` + command line flag or :confval:`follow_untyped_imports` config file option to True. + This option can be specified on a per-module basis as well:: + + # mypy.ini + [mypy-untyped_package.*] + follow_untyped_imports = True + + # pyproject.toml + [[tool.mypy.overrides]] + module = ["untyped_package.*"] + follow_untyped_imports = true + If you are unable to find any existing type hints nor have time to write your own, you can instead *suppress* the errors. @@ -295,9 +314,15 @@ not catch errors in its use. all import errors associated with that library and that library alone by adding the following section to your config file:: + # mypy.ini [mypy-foobar.*] ignore_missing_imports = True + # pyproject.toml + [[tool.mypy.overrides]] + module = ["foobar.*"] + ignore_missing_imports = true + Note: this option is equivalent to adding a ``# type: ignore`` to every import of ``foobar`` in your codebase. For more information, see the documentation about configuring @@ -311,9 +336,13 @@ not catch errors in its use. You can also set :confval:`disable_error_code`, like so:: + # mypy.ini [mypy] disable_error_code = import-untyped + # pyproject.toml + [tool.mypy] + disable_error_code = ["import-untyped"] You can also set the :option:`--ignore-missing-imports ` command line flag or set the :confval:`ignore_missing_imports` config file @@ -503,7 +532,7 @@ accepts one of four string values: main.py:1: note: (Using --follow-imports=error, module not passed on command line) If you are starting a new codebase and plan on using type hints from -the start, we recommend you use either :option:`--follow-imports=normal ` +the start, we **recommend** you use either :option:`--follow-imports=normal ` (the default) or :option:`--follow-imports=error `. Either option will help make sure you are not skipping checking any part of your codebase by accident. @@ -514,16 +543,27 @@ files that do not use type hints) pass under :option:`--follow-imports=normal `. Even if -mypy is unable to perfectly type check a file, it can still glean some +Only if doing this is intractable, try passing mypy just the files +you want to type check and using :option:`--follow-imports=silent `. +Even if mypy is unable to perfectly type check a file, it can still glean some useful information by parsing it (for example, understanding what methods a given object has). See :ref:`existing-code` for more recommendations. -We do not recommend using ``skip`` unless you know what you are doing: -while this option can be quite powerful, it can also cause many -hard-to-debug errors. - Adjusting import following behaviour is often most useful when restricted to specific modules. This can be accomplished by setting a per-module :confval:`follow_imports` config option. + +.. warning:: + + We do not recommend using ``follow_imports=skip`` unless you're really sure + you know what you are doing. This option greatly restricts the analysis mypy + can perform and you will lose a lot of the benefits of type checking. + + This is especially true at the global level. Setting a per-module + ``follow_imports=skip`` for a specific problematic module can be + useful without causing too much harm. + +.. note:: + + If you're looking to resolve import errors related to libraries, try following + the advice in :ref:`fix-missing-imports` before messing with ``follow_imports``. diff --git a/docs/source/runtime_troubles.rst b/docs/source/runtime_troubles.rst index 66ab7b3a84c77..d039db30f3fa6 100644 --- a/docs/source/runtime_troubles.rst +++ b/docs/source/runtime_troubles.rst @@ -21,8 +21,9 @@ problems you may encounter. String literal types and type comments -------------------------------------- -Mypy allows you to add type annotations using ``# type:`` type comments. -For example: +Mypy lets you add type annotations using the (now deprecated) ``# type:`` +type comment syntax. These were required with Python versions older than 3.6, +since they didn't support type annotations on variables. Example: .. code-block:: python @@ -69,7 +70,7 @@ Future annotations import (PEP 563) ----------------------------------- Many of the issues described here are caused by Python trying to evaluate -annotations. Future Python versions (potentially Python 3.12) will by default no +annotations. Future Python versions (potentially Python 3.14) will by default no longer attempt to evaluate function and variable annotations. This behaviour is made available in Python 3.7 and later through the use of ``from __future__ import annotations``. @@ -84,7 +85,7 @@ required to be valid Python syntax. For more details, see :pep:`563`. still require string literals or result in errors, typically involving use of forward references or generics in: - * :ref:`type aliases `; + * :ref:`type aliases ` not defined using the ``type`` statement; * :ref:`type narrowing `; * type definitions (see :py:class:`~typing.TypeVar`, :py:class:`~typing.NewType`, :py:class:`~typing.NamedTuple`); * base classes. @@ -93,6 +94,7 @@ required to be valid Python syntax. For more details, see :pep:`563`. # base class example from __future__ import annotations + class A(tuple['B', 'C']): ... # String literal types needed here class B: ... class C: ... @@ -244,7 +246,8 @@ complicated and you need to use :ref:`typing.TYPE_CHECKING task_queue: Tasks reveal_type(task_queue.get()) # Reveals str -If your subclass is also generic, you can use the following: +If your subclass is also generic, you can use the following (using the +legacy syntax for generic classes): .. code-block:: python @@ -262,9 +265,11 @@ If your subclass is also generic, you can use the following: task_queue: MyQueue[str] reveal_type(task_queue.get()) # Reveals str -In Python 3.9, we can just inherit directly from ``Queue[str]`` or ``Queue[T]`` +In Python 3.9 and later, we can just inherit directly from ``Queue[str]`` or ``Queue[T]`` since its :py:class:`queue.Queue` implements :py:meth:`~object.__class_getitem__`, so -the class object can be subscripted at runtime without issue. +the class object can be subscripted at runtime. You may still encounter issues (even if +you use a recent Python version) when subclassing generic classes defined in third-party +libraries if types are generic only in stubs. Using types defined in stubs but not at runtime ----------------------------------------------- @@ -315,8 +320,8 @@ notes at :ref:`future annotations import`. Using X | Y syntax for Unions ----------------------------- -Starting with Python 3.10 (:pep:`604`), you can spell union types as ``x: int | -str``, instead of ``x: typing.Union[int, str]``. +Starting with Python 3.10 (:pep:`604`), you can spell union types as +``x: int | str``, instead of ``x: typing.Union[int, str]``. There is limited support for using this syntax in Python 3.7 and later as well: if you use ``from __future__ import annotations``, mypy will understand this diff --git a/docs/source/type_inference_and_annotations.rst b/docs/source/type_inference_and_annotations.rst index 6adb4e6512242..318ca4cd91608 100644 --- a/docs/source/type_inference_and_annotations.rst +++ b/docs/source/type_inference_and_annotations.rst @@ -41,12 +41,10 @@ variable type annotation: .. code-block:: python - from typing import Union - - x: Union[int, str] = 1 + x: int | str = 1 Without the type annotation, the type of ``x`` would be just ``int``. We -use an annotation to give it a more general type ``Union[int, str]`` (this +use an annotation to give it a more general type ``int | str`` (this type means that the value can be either an ``int`` or a ``str``). The best way to think about this is that the type annotation sets the type of @@ -55,8 +53,8 @@ about the following code: .. code-block:: python - x: Union[int, str] = 1.1 # error: Incompatible types in assignment - # (expression has type "float", variable has type "Union[int, str]") + x: int | str = 1.1 # error: Incompatible types in assignment + # (expression has type "float", variable has type "int | str") .. note:: diff --git a/docs/source/type_narrowing.rst b/docs/source/type_narrowing.rst index 4c5c2851edd06..697a1519a6032 100644 --- a/docs/source/type_narrowing.rst +++ b/docs/source/type_narrowing.rst @@ -114,7 +114,7 @@ So, we know what ``callable()`` will return. For example: .. code-block:: python - from typing import Callable + from collections.abc import Callable x: Callable[[], int] @@ -123,14 +123,14 @@ So, we know what ``callable()`` will return. For example: else: ... # Will never be executed and will raise error with `--warn-unreachable` -``callable`` function can even split ``Union`` type -for callable and non-callable parts: +The ``callable`` function can even split union types into +callable and non-callable parts: .. code-block:: python - from typing import Callable, Union + from collections.abc import Callable - x: Union[int, Callable[[], int]] + x: int | Callable[[], int] if callable(x): reveal_type(x) # N: Revealed type is "def () -> builtins.int" @@ -255,16 +255,13 @@ to the type specified as the first type parameter (``list[str]``). Generic TypeGuards ~~~~~~~~~~~~~~~~~~ -``TypeGuard`` can also work with generic types: +``TypeGuard`` can also work with generic types (Python 3.12 syntax): .. code-block:: python - from typing import TypeVar from typing import TypeGuard # use `typing_extensions` for `python<3.10` - _T = TypeVar("_T") - - def is_two_element_tuple(val: tuple[_T, ...]) -> TypeGuard[tuple[_T, _T]]: + def is_two_element_tuple[T](val: tuple[T, ...]) -> TypeGuard[tuple[T, T]]: return len(val) == 2 def func(names: tuple[str, ...]): @@ -276,16 +273,13 @@ Generic TypeGuards TypeGuards with parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~ -Type guard functions can accept extra arguments: +Type guard functions can accept extra arguments (Python 3.12 syntax): .. code-block:: python - from typing import Type, TypeVar from typing import TypeGuard # use `typing_extensions` for `python<3.10` - _T = TypeVar("_T") - - def is_set_of(val: set[Any], type: Type[_T]) -> TypeGuard[set[_T]]: + def is_set_of[T](val: set[Any], type: type[T]) -> TypeGuard[set[T]]: return all(isinstance(x, type) for x in val) items: set[Any] @@ -368,14 +362,18 @@ Limitations Mypy's analysis is limited to individual symbols and it will not track relationships between symbols. For example, in the following code it's easy to deduce that if :code:`a` is None then :code:`b` must not be, -therefore :code:`a or b` will always be a string, but Mypy will not be able to tell that: +therefore :code:`a or b` will always be an instance of :code:`C`, +but Mypy will not be able to tell that: .. code-block:: python - def f(a: str | None, b: str | None) -> str: + class C: + pass + + def f(a: C | None, b: C | None) -> C: if a is not None or b is not None: - return a or b # Incompatible return value type (got "str | None", expected "str") - return 'spam' + return a or b # Incompatible return value type (got "C | None", expected "C") + return C() Tracking these sort of cross-variable conditions in a type checker would add significant complexity and performance overhead. @@ -385,9 +383,175 @@ or rewrite the function to be slightly more verbose: .. code-block:: python - def f(a: str | None, b: str | None) -> str: + def f(a: C | None, b: C | None) -> C: if a is not None: return a elif b is not None: return b - return 'spam' + return C() + + +.. _typeis: + +TypeIs +------ + +Mypy supports TypeIs (:pep:`742`). + +A `TypeIs narrowing function `_ +allows you to define custom type checks that can narrow the type of a variable +in `both the if and else `_ +branches of a conditional, similar to how the built-in isinstance() function works. + +TypeIs is new in Python 3.13 — for use in older Python versions, use the backport +from `typing_extensions `_ + +Consider the following example using TypeIs: + +.. code-block:: python + + from typing import TypeIs + + def is_str(x: object) -> TypeIs[str]: + return isinstance(x, str) + + def process(x: int | str) -> None: + if is_str(x): + reveal_type(x) # Revealed type is 'str' + print(x.upper()) # Valid: x is str + else: + reveal_type(x) # Revealed type is 'int' + print(x + 1) # Valid: x is int + +In this example, the function is_str is a type narrowing function +that returns TypeIs[str]. When used in an if statement, x is narrowed +to str in the if branch and to int in the else branch. + +Key points: + + +- The function must accept at least one positional argument. + +- The return type is annotated as ``TypeIs[T]``, where ``T`` is the type you + want to narrow to. + +- The function must return a ``bool`` value. + +- In the ``if`` branch (when the function returns ``True``), the type of the + argument is narrowed to the intersection of its original type and ``T``. + +- In the ``else`` branch (when the function returns ``False``), the type of + the argument is narrowed to the intersection of its original type and the + complement of ``T``. + + +TypeIs vs TypeGuard +~~~~~~~~~~~~~~~~~~~ + +While both TypeIs and TypeGuard allow you to define custom type narrowing +functions, they differ in important ways: + +- **Type narrowing behavior**: TypeIs narrows the type in both the if and else branches, + whereas TypeGuard narrows only in the if branch. + +- **Compatibility requirement**: TypeIs requires that the narrowed type T be + compatible with the input type of the function. TypeGuard does not have this restriction. + +- **Type inference**: With TypeIs, the type checker may infer a more precise type by + combining existing type information with T. + +Here's an example demonstrating the behavior with TypeGuard: + +.. code-block:: python + + from typing import TypeGuard, reveal_type + + def is_str(x: object) -> TypeGuard[str]: + return isinstance(x, str) + + def process(x: int | str) -> None: + if is_str(x): + reveal_type(x) # Revealed type is "builtins.str" + print(x.upper()) # ok: x is str + else: + reveal_type(x) # Revealed type is "Union[builtins.int, builtins.str]" + print(x + 1) # ERROR: Unsupported operand types for + ("str" and "int") [operator] + +Generic TypeIs +~~~~~~~~~~~~~~ + +``TypeIs`` functions can also work with generic types: + +.. code-block:: python + + from typing import TypeVar, TypeIs + + T = TypeVar('T') + + def is_two_element_tuple(val: tuple[T, ...]) -> TypeIs[tuple[T, T]]: + return len(val) == 2 + + def process(names: tuple[str, ...]) -> None: + if is_two_element_tuple(names): + reveal_type(names) # Revealed type is 'tuple[str, str]' + else: + reveal_type(names) # Revealed type is 'tuple[str, ...]' + + +TypeIs with Additional Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +TypeIs functions can accept additional parameters beyond the first. +The type narrowing applies only to the first argument. + +.. code-block:: python + + from typing import Any, TypeVar, reveal_type, TypeIs + + T = TypeVar('T') + + def is_instance_of(val: Any, typ: type[T]) -> TypeIs[T]: + return isinstance(val, typ) + + def process(x: Any) -> None: + if is_instance_of(x, int): + reveal_type(x) # Revealed type is 'int' + print(x + 1) # ok + else: + reveal_type(x) # Revealed type is 'Any' + +TypeIs in Methods +~~~~~~~~~~~~~~~~~ + +A method can also serve as a ``TypeIs`` function. Note that in instance or +class methods, the type narrowing applies to the second parameter +(after ``self`` or ``cls``). + +.. code-block:: python + + class Validator: + def is_valid(self, instance: object) -> TypeIs[str]: + return isinstance(instance, str) + + def process(self, to_validate: object) -> None: + if Validator().is_valid(to_validate): + reveal_type(to_validate) # Revealed type is 'str' + print(to_validate.upper()) # ok: to_validate is str + + +Assignment Expressions with TypeIs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the assignment expression operator ``:=`` with ``TypeIs`` to create a new variable and narrow its type simultaneously. + +.. code-block:: python + + from typing import TypeIs, reveal_type + + def is_float(x: object) -> TypeIs[float]: + return isinstance(x, float) + + def main(a: object) -> None: + if is_float(x := a): + reveal_type(x) # Revealed type is 'float' + # x is narrowed to float in this block + print(x + 1.0) diff --git a/docs/source/typed_dict.rst b/docs/source/typed_dict.rst index e5ce2927db4d1..bbb10a12abe8f 100644 --- a/docs/source/typed_dict.rst +++ b/docs/source/typed_dict.rst @@ -89,7 +89,7 @@ A ``TypedDict`` object is not a subtype of the regular ``dict[...]`` type (and vice versa), since :py:class:`dict` allows arbitrary keys to be added and removed, unlike ``TypedDict``. However, any ``TypedDict`` object is a subtype of (that is, compatible with) ``Mapping[str, object]``, since -:py:class:`~typing.Mapping` only provides read-only access to the dictionary items: +:py:class:`~collections.abc.Mapping` only provides read-only access to the dictionary items: .. code-block:: python @@ -158,7 +158,7 @@ You must use string literals as keys when calling most of the methods, as otherwise mypy won't be able to check that the key is valid. List of supported operations: -* Anything included in :py:class:`~typing.Mapping`: +* Anything included in :py:class:`~collections.abc.Mapping`: * ``d[key]`` * ``key in d`` @@ -236,6 +236,46 @@ another ``TypedDict`` if all required keys in the other ``TypedDict`` are requir first ``TypedDict``, and all non-required keys of the other ``TypedDict`` are also non-required keys in the first ``TypedDict``. +Read-only items +--------------- + +You can use ``typing.ReadOnly``, introduced in Python 3.13, or +``typing_extensions.ReadOnly`` to mark TypedDict items as read-only (:pep:`705`): + +.. code-block:: python + + from typing import TypedDict + + # Or "from typing ..." on Python 3.13+ + from typing_extensions import ReadOnly + + class Movie(TypedDict): + name: ReadOnly[str] + num_watched: int + + m: Movie = {"name": "Jaws", "num_watched": 1} + m["name"] = "The Godfather" # Error: "name" is read-only + m["num_watched"] += 1 # OK + +A TypedDict with a mutable item can be assigned to a TypedDict +with a corresponding read-only item, and the type of the item can +vary :ref:`covariantly `: + +.. code-block:: python + + class Entry(TypedDict): + name: ReadOnly[str | None] + year: ReadOnly[int] + + class Movie(TypedDict): + name: str + year: int + + def process_entry(i: Entry) -> None: ... + + m: Movie = {"name": "Jaws", "year": 1975} + process_entry(m) # OK + Unions of TypedDicts -------------------- @@ -248,3 +288,41 @@ section of the docs has a full description with an example, but in short, you wi need to give each TypedDict the same key where each value has a unique :ref:`Literal type `. Then, check that key to distinguish between your TypedDicts. + +Inline TypedDict types +---------------------- + +.. note:: + + This is an experimental (non-standard) feature. Use + ``--enable-incomplete-feature=InlineTypedDict`` to enable. + +Sometimes you may want to define a complex nested JSON schema, or annotate +a one-off function that returns a TypedDict. In such cases it may be convenient +to use inline TypedDict syntax. For example: + +.. code-block:: python + + def test_values() -> {"int": int, "str": str}: + return {"int": 42, "str": "test"} + + class Response(TypedDict): + status: int + msg: str + # Using inline syntax here avoids defining two additional TypedDicts. + content: {"items": list[{"key": str, "value": str}]} + +Inline TypedDicts can also by used as targets of type aliases, but due to +ambiguity with a regular variables it is only allowed for (newer) explicit +type alias forms: + +.. code-block:: python + + from typing import TypeAlias + + X = {"a": int, "b": int} # creates a variable with type dict[str, type[int]] + Y: TypeAlias = {"a": int, "b": int} # creates a type alias + type Z = {"a": int, "b": int} # same as above (Python 3.12+ only) + +Also, due to incompatibility with runtime type-checking it is strongly recommended +to *not* use inline syntax in union types. diff --git a/misc/apply-cache-diff.py b/misc/apply-cache-diff.py index 29c55247de92a..8ede9766bd06d 100644 --- a/misc/apply-cache-diff.py +++ b/misc/apply-cache-diff.py @@ -8,13 +8,13 @@ from __future__ import annotations import argparse -import json import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mypy.metastore import FilesystemMetadataStore, MetadataStore, SqliteMetadataStore +from mypy.util import json_dumps, json_loads def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: @@ -26,10 +26,10 @@ def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: def apply_diff(cache_dir: str, diff_file: str, sqlite: bool = False) -> None: cache = make_cache(cache_dir, sqlite) - with open(diff_file) as f: - diff = json.load(f) + with open(diff_file, "rb") as f: + diff = json_loads(f.read()) - old_deps = json.loads(cache.read("@deps.meta.json")) + old_deps = json_loads(cache.read("@deps.meta.json")) for file, data in diff.items(): if data is None: @@ -37,10 +37,10 @@ def apply_diff(cache_dir: str, diff_file: str, sqlite: bool = False) -> None: else: cache.write(file, data) if file.endswith(".meta.json") and "@deps" not in file: - meta = json.loads(data) + meta = json_loads(data) old_deps["snapshot"][meta["id"]] = meta["hash"] - cache.write("@deps.meta.json", json.dumps(old_deps)) + cache.write("@deps.meta.json", json_dumps(old_deps)) cache.commit() diff --git a/misc/diff-cache.py b/misc/diff-cache.py index 15d3e5a83983a..8441caf81304e 100644 --- a/misc/diff-cache.py +++ b/misc/diff-cache.py @@ -8,7 +8,6 @@ from __future__ import annotations import argparse -import json import os import sys from collections import defaultdict @@ -17,6 +16,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mypy.metastore import FilesystemMetadataStore, MetadataStore, SqliteMetadataStore +from mypy.util import json_dumps, json_loads def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: @@ -33,7 +33,7 @@ def merge_deps(all: dict[str, set[str]], new: dict[str, set[str]]) -> None: def load(cache: MetadataStore, s: str) -> Any: data = cache.read(s) - obj = json.loads(data) + obj = json_loads(data) if s.endswith(".meta.json"): # For meta files, zero out the mtimes and sort the # dependencies to avoid spurious conflicts @@ -73,7 +73,7 @@ def main() -> None: type_misses: dict[str, int] = defaultdict(int) type_hits: dict[str, int] = defaultdict(int) - updates: dict[str, str | None] = {} + updates: dict[str, bytes | None] = {} deps1: dict[str, set[str]] = {} deps2: dict[str, set[str]] = {} @@ -96,7 +96,7 @@ def main() -> None: # so we can produce a much smaller direct diff of them. if ".deps." not in s: if obj2 is not None: - updates[s] = json.dumps(obj2) + updates[s] = json_dumps(obj2) else: updates[s] = None elif obj2: @@ -122,7 +122,7 @@ def main() -> None: merge_deps(new_deps, root_deps) new_deps_json = {k: list(v) for k, v in new_deps.items() if v} - updates["@root.deps.json"] = json.dumps(new_deps_json) + updates["@root.deps.json"] = json_dumps(new_deps_json) # Drop updates to deps.meta.json for size reasons. The diff # applier will manually fix it up. @@ -136,8 +136,8 @@ def main() -> None: print("hits", type_hits) print("misses", type_misses) - with open(args.output, "w") as f: - json.dump(updates, f) + with open(args.output, "wb") as f: + f.write(json_dumps(updates)) if __name__ == "__main__": diff --git a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch index 683b0c322b710..91e255242ee93 100644 --- a/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch +++ b/misc/typeshed_patches/0001-Remove-use-of-LiteralString-in-builtins-13743.patch @@ -1,17 +1,17 @@ -From 3ec9b878d6bbe3fae64a508a62372f10a886406f Mon Sep 17 00:00:00 2001 +From b4259edd94188f9e4cc77a22e768eea183a32053 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:55:07 -0700 Subject: [PATCH] Remove use of LiteralString in builtins (#13743) --- - mypy/typeshed/stdlib/builtins.pyi | 95 ------------------------------- - 1 file changed, 95 deletions(-) + mypy/typeshed/stdlib/builtins.pyi | 100 +----------------------------- + 1 file changed, 1 insertion(+), 99 deletions(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi -index 53e00ec6a..bad3250ef 100644 +index 63c53a5f6..d55042b56 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi -@@ -61,7 +61,6 @@ from typing import ( # noqa: Y022 +@@ -63,7 +63,6 @@ from typing import ( # noqa: Y022 from typing_extensions import ( # noqa: Y023 Concatenate, Literal, @@ -19,7 +19,7 @@ index 53e00ec6a..bad3250ef 100644 ParamSpec, Self, TypeAlias, -@@ -435,31 +434,16 @@ class str(Sequence[str]): +@@ -438,31 +437,16 @@ class str(Sequence[str]): def __new__(cls, object: object = ...) -> Self: ... @overload def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... @@ -51,7 +51,7 @@ index 53e00ec6a..bad3250ef 100644 def format(self, *args: object, **kwargs: object) -> str: ... def format_map(self, mapping: _FormatMapMapping, /) -> str: ... def index(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ... -@@ -475,99 +459,35 @@ class str(Sequence[str]): +@@ -478,99 +462,35 @@ class str(Sequence[str]): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... @@ -151,7 +151,7 @@ index 53e00ec6a..bad3250ef 100644 def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] @staticmethod @overload -@@ -578,9 +498,6 @@ class str(Sequence[str]): +@@ -581,39 +501,21 @@ class str(Sequence[str]): @staticmethod @overload def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ... @@ -161,8 +161,13 @@ index 53e00ec6a..bad3250ef 100644 def __add__(self, value: str, /) -> str: ... # type: ignore[misc] # Incompatible with Sequence.__contains__ def __contains__(self, key: str, /) -> bool: ... # type: ignore[override] -@@ -589,25 +506,13 @@ class str(Sequence[str]): - def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... + def __eq__(self, value: object, /) -> bool: ... + def __ge__(self, value: str, /) -> bool: ... +- @overload +- def __getitem__(self: LiteralString, key: SupportsIndex | slice, /) -> LiteralString: ... +- @overload +- def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... # type: ignore[misc] ++ def __getitem__(self, key: SupportsIndex | slice, /) -> str: ... def __gt__(self, value: str, /) -> bool: ... def __hash__(self) -> int: ... - @overload @@ -188,5 +193,5 @@ index 53e00ec6a..bad3250ef 100644 def __getnewargs__(self) -> tuple[str]: ... -- -2.45.2 +2.47.0 diff --git a/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch new file mode 100644 index 0000000000000..b23461b447a1d --- /dev/null +++ b/misc/typeshed_patches/0001-Revert-Remove-redundant-inheritances-from-Iterator.patch @@ -0,0 +1,324 @@ +From 25250cbe1f7ee0e924ac03b3f19297e1885dd13e Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Sat, 21 Dec 2024 22:36:38 +0100 +Subject: [PATCH] Revert Remove redundant inheritances from Iterator in + builtins + +--- + mypy/typeshed/stdlib/_asyncio.pyi | 4 +- + mypy/typeshed/stdlib/builtins.pyi | 10 ++--- + mypy/typeshed/stdlib/csv.pyi | 4 +- + mypy/typeshed/stdlib/fileinput.pyi | 6 +-- + mypy/typeshed/stdlib/itertools.pyi | 38 +++++++++---------- + mypy/typeshed/stdlib/multiprocessing/pool.pyi | 4 +- + mypy/typeshed/stdlib/sqlite3/__init__.pyi | 2 +- + 7 files changed, 34 insertions(+), 34 deletions(-) + +diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi +index a25902661..18920cd8a 100644 +--- a/mypy/typeshed/stdlib/_asyncio.pyi ++++ b/mypy/typeshed/stdlib/_asyncio.pyi +@@ -1,6 +1,6 @@ + import sys + from asyncio.events import AbstractEventLoop +-from collections.abc import Awaitable, Callable, Coroutine, Generator ++from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable + from contextvars import Context + from types import FrameType + from typing import Any, Literal, TextIO, TypeVar +@@ -13,7 +13,7 @@ _T = TypeVar("_T") + _T_co = TypeVar("_T_co", covariant=True) + _TaskYieldType: TypeAlias = Future[object] | None + +-class Future(Awaitable[_T]): ++class Future(Awaitable[_T], Iterable[_T]): + _state: str + @property + def _exception(self) -> BaseException | None: ... +diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi +index 5c6d321f7..56a5969d1 100644 +--- a/mypy/typeshed/stdlib/builtins.pyi ++++ b/mypy/typeshed/stdlib/builtins.pyi +@@ -1130,7 +1130,7 @@ class frozenset(AbstractSet[_T_co]): + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +-class enumerate(Generic[_T]): ++class enumerate(Iterator[tuple[int, _T]]): + def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[int, _T]: ... +@@ -1324,7 +1324,7 @@ else: + + exit: _sitebuiltins.Quitter + +-class filter(Generic[_T]): ++class filter(Iterator[_T]): + @overload + def __new__(cls, function: None, iterable: Iterable[_T | None], /) -> Self: ... + @overload +@@ -1389,7 +1389,7 @@ license: _sitebuiltins._Printer + + def locals() -> dict[str, Any]: ... + +-class map(Generic[_S]): ++class map(Iterator[_S]): + @overload + def __new__(cls, func: Callable[[_T1], _S], iter1: Iterable[_T1], /) -> Self: ... + @overload +@@ -1632,7 +1632,7 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex + + quit: _sitebuiltins.Quitter + +-class reversed(Generic[_T]): ++class reversed(Iterator[_T]): + @overload + def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc] + @overload +@@ -1693,7 +1693,7 @@ def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... + @overload + def vars(object: Any = ..., /) -> dict[str, Any]: ... + +-class zip(Generic[_T_co]): ++class zip(Iterator[_T_co]): + if sys.version_info >= (3, 10): + @overload + def __new__(cls, *, strict: bool = ...) -> zip[Any]: ... +diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi +index 4a82de638..ef93129d6 100644 +--- a/mypy/typeshed/stdlib/csv.pyi ++++ b/mypy/typeshed/stdlib/csv.pyi +@@ -25,7 +25,7 @@ else: + from _csv import _reader as Reader, _writer as Writer + + from _typeshed import SupportsWrite +-from collections.abc import Collection, Iterable, Mapping, Sequence ++from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence + from typing import Any, Generic, Literal, TypeVar, overload + from typing_extensions import Self + +@@ -75,7 +75,7 @@ class excel(Dialect): ... + class excel_tab(excel): ... + class unix_dialect(Dialect): ... + +-class DictReader(Generic[_T]): ++class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): + fieldnames: Sequence[_T] | None + restkey: _T | None + restval: str | Any | None +diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi +index bf6daad0a..1e6aa78e2 100644 +--- a/mypy/typeshed/stdlib/fileinput.pyi ++++ b/mypy/typeshed/stdlib/fileinput.pyi +@@ -1,8 +1,8 @@ + import sys + from _typeshed import AnyStr_co, StrOrBytesPath +-from collections.abc import Callable, Iterable ++from collections.abc import Callable, Iterable, Iterator + from types import TracebackType +-from typing import IO, Any, AnyStr, Generic, Literal, Protocol, overload ++from typing import IO, Any, AnyStr, Literal, Protocol, overload + from typing_extensions import Self, TypeAlias + + if sys.version_info >= (3, 9): +@@ -107,7 +107,7 @@ def fileno() -> int: ... + def isfirstline() -> bool: ... + def isstdin() -> bool: ... + +-class FileInput(Generic[AnyStr]): ++class FileInput(Iterator[AnyStr]): + if sys.version_info >= (3, 10): + # encoding and errors are added + @overload +diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi +index 013c3cba1..f69665882 100644 +--- a/mypy/typeshed/stdlib/itertools.pyi ++++ b/mypy/typeshed/stdlib/itertools.pyi +@@ -29,7 +29,7 @@ _Predicate: TypeAlias = Callable[[_T], object] + + # Technically count can take anything that implements a number protocol and has an add method + # but we can't enforce the add method +-class count(Generic[_N]): ++class count(Iterator[_N]): + @overload + def __new__(cls) -> count[int]: ... + @overload +@@ -39,12 +39,12 @@ class count(Generic[_N]): + def __next__(self) -> _N: ... + def __iter__(self) -> Self: ... + +-class cycle(Generic[_T]): ++class cycle(Iterator[_T]): + def __init__(self, iterable: Iterable[_T], /) -> None: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... + +-class repeat(Generic[_T]): ++class repeat(Iterator[_T]): + @overload + def __init__(self, object: _T) -> None: ... + @overload +@@ -53,7 +53,7 @@ class repeat(Generic[_T]): + def __iter__(self) -> Self: ... + def __length_hint__(self) -> int: ... + +-class accumulate(Generic[_T]): ++class accumulate(Iterator[_T]): + @overload + def __init__(self, iterable: Iterable[_T], func: None = None, *, initial: _T | None = ...) -> None: ... + @overload +@@ -61,7 +61,7 @@ class accumulate(Generic[_T]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class chain(Generic[_T]): ++class chain(Iterator[_T]): + def __init__(self, *iterables: Iterable[_T]) -> None: ... + def __next__(self) -> _T: ... + def __iter__(self) -> Self: ... +@@ -71,22 +71,22 @@ class chain(Generic[_T]): + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +-class compress(Generic[_T]): ++class compress(Iterator[_T]): + def __init__(self, data: Iterable[_T], selectors: Iterable[Any]) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class dropwhile(Generic[_T]): ++class dropwhile(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class filterfalse(Generic[_T]): ++class filterfalse(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T] | None, iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class groupby(Generic[_T_co, _S_co]): ++class groupby(Iterator[tuple[_T_co, Iterator[_S_co]]], Generic[_T_co, _S_co]): + @overload + def __new__(cls, iterable: Iterable[_T1], key: None = None) -> groupby[_T1, _T1]: ... + @overload +@@ -94,7 +94,7 @@ class groupby(Generic[_T_co, _S_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[_T_co, Iterator[_S_co]]: ... + +-class islice(Generic[_T]): ++class islice(Iterator[_T]): + @overload + def __init__(self, iterable: Iterable[_T], stop: int | None, /) -> None: ... + @overload +@@ -102,19 +102,19 @@ class islice(Generic[_T]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + +-class starmap(Generic[_T_co]): ++class starmap(Iterator[_T_co]): + def __new__(cls, function: Callable[..., _T], iterable: Iterable[Iterable[Any]], /) -> starmap[_T]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class takewhile(Generic[_T]): ++class takewhile(Iterator[_T]): + def __init__(self, predicate: _Predicate[_T], iterable: Iterable[_T], /) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T: ... + + def tee(iterable: Iterable[_T], n: int = 2, /) -> tuple[Iterator[_T], ...]: ... + +-class zip_longest(Generic[_T_co]): ++class zip_longest(Iterator[_T_co]): + # one iterable (fillvalue doesn't matter) + @overload + def __new__(cls, iter1: Iterable[_T1], /, *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... +@@ -192,7 +192,7 @@ class zip_longest(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class product(Generic[_T_co]): ++class product(Iterator[_T_co]): + @overload + def __new__(cls, iter1: Iterable[_T1], /) -> product[tuple[_T1]]: ... + @overload +@@ -277,7 +277,7 @@ class product(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class permutations(Generic[_T_co]): ++class permutations(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> permutations[tuple[_T, _T]]: ... + @overload +@@ -291,7 +291,7 @@ class permutations(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class combinations(Generic[_T_co]): ++class combinations(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations[tuple[_T, _T]]: ... + @overload +@@ -305,7 +305,7 @@ class combinations(Generic[_T_co]): + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + +-class combinations_with_replacement(Generic[_T_co]): ++class combinations_with_replacement(Iterator[_T_co]): + @overload + def __new__(cls, iterable: Iterable[_T], r: Literal[2]) -> combinations_with_replacement[tuple[_T, _T]]: ... + @overload +@@ -320,13 +320,13 @@ class combinations_with_replacement(Generic[_T_co]): + def __next__(self) -> _T_co: ... + + if sys.version_info >= (3, 10): +- class pairwise(Generic[_T_co]): ++ class pairwise(Iterator[_T_co]): + def __new__(cls, iterable: Iterable[_T], /) -> pairwise[tuple[_T, _T]]: ... + def __iter__(self) -> Self: ... + def __next__(self) -> _T_co: ... + + if sys.version_info >= (3, 12): +- class batched(Generic[_T_co]): ++ class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): + if sys.version_info >= (3, 13): + def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... + else: +diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi +index 61d6d0781..950ed1d8c 100644 +--- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi ++++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi +@@ -1,5 +1,5 @@ + import sys +-from collections.abc import Callable, Iterable, Mapping ++from collections.abc import Callable, Iterable, Iterator, Mapping + from types import TracebackType + from typing import Any, Final, Generic, TypeVar + from typing_extensions import Self +@@ -36,7 +36,7 @@ class MapResult(ApplyResult[list[_T]]): + error_callback: Callable[[BaseException], object] | None, + ) -> None: ... + +-class IMapIterator(Generic[_T]): ++class IMapIterator(Iterator[_T]): + def __init__(self, pool: Pool) -> None: ... + def __iter__(self) -> Self: ... + def next(self, timeout: float | None = None) -> _T: ... +diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi +index bc0ff6469..730404bde 100644 +--- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi ++++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi +@@ -397,7 +397,7 @@ class Connection: + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / + ) -> Literal[False]: ... + +-class Cursor: ++class Cursor(Iterator[Any]): + arraysize: int + @property + def connection(self) -> Connection: ... +-- +2.47.1 diff --git a/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch index 044e672bfda5e..331628af14243 100644 --- a/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch +++ b/misc/typeshed_patches/0001-Revert-sum-literal-integer-change-13961.patch @@ -1,4 +1,4 @@ -From 44bc98bd50e7170887f0740b53ed95a8eb04f00e Mon Sep 17 00:00:00 2001 +From 58c6a6ab863c1c38e95ccafaf13792ed9c00e499 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 29 Oct 2022 12:47:21 -0700 Subject: [PATCH] Revert sum literal integer change (#13961) @@ -19,18 +19,18 @@ within mypy, I might pursue upstreaming this in typeshed. 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi -index 99919c64c..680cd5561 100644 +index ea9f8c894..a6065cc67 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi -@@ -1596,7 +1596,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit +@@ -1653,7 +1653,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload --def sum(iterable: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] -+def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] +-def sum(iterable: Iterable[bool | _LiteralInteger], /, start: int = 0) -> int: ... ++def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... @overload def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload -- -2.39.3 (Apple Git-146) +2.46.0 diff --git a/mypy/applytype.py b/mypy/applytype.py index 783748cd8a5eb..e88947cc64304 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -215,6 +215,7 @@ def __init__( bound_tvars: frozenset[TypeVarLikeType] = frozenset(), seen_aliases: frozenset[TypeInfo] = frozenset(), ) -> None: + super().__init__() self.poly_tvars = set(poly_tvars) # This is a simplified version of TypeVarScope used during semantic analysis. self.bound_tvars = bound_tvars diff --git a/mypy/binder.py b/mypy/binder.py index 9d0a33b54bc29..52ae9774e6d4b 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -2,7 +2,7 @@ from collections import defaultdict from contextlib import contextmanager -from typing import DefaultDict, Iterator, List, Optional, Tuple, Union, cast +from typing import DefaultDict, Iterator, List, NamedTuple, Optional, Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy.erasetype import remove_instance_last_known_values @@ -30,6 +30,11 @@ BindableExpression: _TypeAlias = Union[IndexExpr, MemberExpr, NameExpr] +class CurrentType(NamedTuple): + type: Type + from_assignment: bool + + class Frame: """A Frame represents a specific point in the execution of a program. It carries information about the current types of expressions at @@ -44,7 +49,7 @@ class Frame: def __init__(self, id: int, conditional_frame: bool = False) -> None: self.id = id - self.types: dict[Key, Type] = {} + self.types: dict[Key, CurrentType] = {} self.unreachable = False self.conditional_frame = conditional_frame self.suppress_unreachable_warnings = False @@ -132,10 +137,10 @@ def push_frame(self, conditional_frame: bool = False) -> Frame: self.options_on_return.append([]) return f - def _put(self, key: Key, type: Type, index: int = -1) -> None: - self.frames[index].types[key] = type + def _put(self, key: Key, type: Type, from_assignment: bool, index: int = -1) -> None: + self.frames[index].types[key] = CurrentType(type, from_assignment) - def _get(self, key: Key, index: int = -1) -> Type | None: + def _get(self, key: Key, index: int = -1) -> CurrentType | None: if index < 0: index += len(self.frames) for i in range(index, -1, -1): @@ -143,7 +148,7 @@ def _get(self, key: Key, index: int = -1) -> Type | None: return self.frames[i].types[key] return None - def put(self, expr: Expression, typ: Type) -> None: + def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> None: if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): return if not literal(expr): @@ -153,7 +158,7 @@ def put(self, expr: Expression, typ: Type) -> None: if key not in self.declarations: self.declarations[key] = get_declaration(expr) self._add_dependencies(key) - self._put(key, typ) + self._put(key, typ, from_assignment) def unreachable(self) -> None: self.frames[-1].unreachable = True @@ -164,7 +169,10 @@ def suppress_unreachable_warnings(self) -> None: def get(self, expr: Expression) -> Type | None: key = literal_hash(expr) assert key is not None, "Internal error: binder tried to get non-literal" - return self._get(key) + found = self._get(key) + if found is None: + return None + return found.type def is_unreachable(self) -> bool: # TODO: Copy the value of unreachable into new frames to avoid @@ -193,7 +201,7 @@ def update_from_options(self, frames: list[Frame]) -> bool: If a key is declared as AnyType, only update it if all the options are the same. """ - + all_reachable = all(not f.unreachable for f in frames) frames = [f for f in frames if not f.unreachable] changed = False keys = {key for f in frames for key in f.types} @@ -207,17 +215,30 @@ def update_from_options(self, frames: list[Frame]) -> bool: # know anything about key in at least one possible frame. continue - type = resulting_values[0] - assert type is not None + if all_reachable and all( + x is not None and not x.from_assignment for x in resulting_values + ): + # Do not synthesize a new type if we encountered a conditional block + # (if, while or match-case) without assignments. + # See check-isinstance.test::testNoneCheckDoesNotMakeTypeVarOptional + # This is a safe assumption: the fact that we checked something with `is` + # or `isinstance` does not change the type of the value. + continue + + current_type = resulting_values[0] + assert current_type is not None + type = current_type.type declaration_type = get_proper_type(self.declarations.get(key)) if isinstance(declaration_type, AnyType): # At this point resulting values can't contain None, see continue above - if not all(is_same_type(type, cast(Type, t)) for t in resulting_values[1:]): + if not all( + t is not None and is_same_type(type, t.type) for t in resulting_values[1:] + ): type = AnyType(TypeOfAny.from_another_any, source_any=declaration_type) else: for other in resulting_values[1:]: assert other is not None - type = join_simple(self.declarations[key], type, other) + type = join_simple(self.declarations[key], type, other.type) # Try simplifying resulting type for unions involving variadic tuples. # Technically, everything is still valid without this step, but if we do # not do this, this may create long unions after exiting an if check like: @@ -236,8 +257,8 @@ def update_from_options(self, frames: list[Frame]) -> bool: ) if simplified == self.declarations[key]: type = simplified - if current_value is None or not is_same_type(type, current_value): - self._put(key, type) + if current_value is None or not is_same_type(type, current_value[0]): + self._put(key, type, from_assignment=True) changed = True self.frames[-1].unreachable = not frames @@ -374,7 +395,9 @@ def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Ty key = literal_hash(expr) assert key is not None enclosers = [get_declaration(expr)] + [ - f.types[key] for f in self.frames if key in f.types and is_subtype(type, f.types[key]) + f.types[key].type + for f in self.frames + if key in f.types and is_subtype(type, f.types[key][0]) ] return enclosers[-1] diff --git a/mypy/build.py b/mypy/build.py index 733f0685792ed..40dd733133351 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -59,7 +59,7 @@ get_mypy_comments, hash_digest, is_stub_package_file, - is_sub_path, + is_sub_path_normabs, is_typeshed_file, module_prefix, read_py_file, @@ -92,9 +92,10 @@ from mypy.plugins.default import DefaultPlugin from mypy.renaming import LimitedVariableRenameVisitor, VariableRenameVisitor from mypy.stats import dump_type_stats -from mypy.stubinfo import legacy_bundled_packages, non_bundled_packages, stub_distribution_name +from mypy.stubinfo import is_module_from_legacy_bundled_package, stub_distribution_name from mypy.types import Type from mypy.typestate import reset_global_state, type_state +from mypy.util import json_dumps, json_loads from mypy.version import __version__ # Switch to True to produce debug output related to fine-grained incremental @@ -664,7 +665,7 @@ def __init__( for module in CORE_BUILTIN_MODULES: if options.use_builtins_fixtures: continue - path = self.find_module_cache.find_module(module) + path = self.find_module_cache.find_module(module, fast_path=True) if not isinstance(path, str): raise CompileError( [f"Failed to find builtin module {module}, perhaps typeshed is broken?"] @@ -736,8 +737,8 @@ def maybe_swap_for_shadow_path(self, path: str) -> str: shadow_file = self.shadow_equivalence_map.get(path) return shadow_file if shadow_file else path - def get_stat(self, path: str) -> os.stat_result: - return self.fscache.stat(self.maybe_swap_for_shadow_path(path)) + def get_stat(self, path: str) -> os.stat_result | None: + return self.fscache.stat_or_none(self.maybe_swap_for_shadow_path(path)) def getmtime(self, path: str) -> int: """Return a file's mtime; but 0 in bazel mode. @@ -858,7 +859,7 @@ def load_fine_grained_deps(self, id: str) -> dict[str, set[str]]: t0 = time.time() if id in self.fg_deps_meta: # TODO: Assert deps file wasn't changed. - deps = json.loads(self.metastore.read(self.fg_deps_meta[id]["path"])) + deps = json_loads(self.metastore.read(self.fg_deps_meta[id]["path"])) else: deps = {} val = {k: set(v) for k, v in deps.items()} @@ -911,8 +912,8 @@ def stats_summary(self) -> Mapping[str, object]: return self.stats -def deps_to_json(x: dict[str, set[str]]) -> str: - return json.dumps({k: list(v) for k, v in x.items()}, separators=(",", ":")) +def deps_to_json(x: dict[str, set[str]]) -> bytes: + return json_dumps({k: list(v) for k, v in x.items()}) # File for storing metadata about all the fine-grained dependency caches @@ -980,7 +981,7 @@ def write_deps_cache( meta = {"snapshot": meta_snapshot, "deps_meta": fg_deps_meta} - if not metastore.write(DEPS_META_FILE, json.dumps(meta, separators=(",", ":"))): + if not metastore.write(DEPS_META_FILE, json_dumps(meta)): manager.log(f"Error writing fine-grained deps meta JSON file {DEPS_META_FILE}") error = True @@ -1048,8 +1049,11 @@ def generate_deps_for_cache(manager: BuildManager, graph: Graph) -> dict[str, di def write_plugins_snapshot(manager: BuildManager) -> None: """Write snapshot of versions and hashes of currently active plugins.""" - snapshot = json.dumps(manager.plugins_snapshot, separators=(",", ":")) - if not manager.metastore.write(PLUGIN_SNAPSHOT_FILE, snapshot): + snapshot = json_dumps(manager.plugins_snapshot) + if ( + not manager.metastore.write(PLUGIN_SNAPSHOT_FILE, snapshot) + and manager.options.cache_dir != os.devnull + ): manager.errors.set_file(_cache_dir_prefix(manager.options), None, manager.options) manager.errors.report(0, 0, "Error writing plugins snapshot", blocker=True) @@ -1079,8 +1083,8 @@ def read_quickstart_file( # just ignore it. raw_quickstart: dict[str, Any] = {} try: - with open(options.quickstart_file) as f: - raw_quickstart = json.load(f) + with open(options.quickstart_file, "rb") as f: + raw_quickstart = json_loads(f.read()) quickstart = {} for file, (x, y, z) in raw_quickstart.items(): @@ -1148,10 +1152,10 @@ def _load_json_file( manager.add_stats(metastore_read_time=time.time() - t0) # Only bother to compute the log message if we are logging it, since it could be big if manager.verbosity() >= 2: - manager.trace(log_success + data.rstrip()) + manager.trace(log_success + data.rstrip().decode()) try: t1 = time.time() - result = json.loads(data) + result = json_loads(data) manager.add_stats(data_json_load_time=time.time() - t1) except json.JSONDecodeError: manager.errors.set_file(file, None, manager.options) @@ -1343,8 +1347,8 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | No # So that plugins can return data with tuples in it without # things silently always invalidating modules, we round-trip # the config data. This isn't beautiful. - plugin_data = json.loads( - json.dumps(manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=True))) + plugin_data = json_loads( + json_dumps(manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=True))) ) if m.plugin_data != plugin_data: manager.log(f"Metadata abandoned for {id}: plugin configuration differs") @@ -1394,9 +1398,9 @@ def validate_meta( if bazel: # Normalize path under bazel to make sure it isn't absolute path = normpath(path, manager.options) - try: - st = manager.get_stat(path) - except OSError: + + st = manager.get_stat(path) + if st is None: return None if not stat.S_ISDIR(st.st_mode) and not stat.S_ISREG(st.st_mode): manager.log(f"Metadata abandoned for {id}: file or directory {path} does not exist") @@ -1478,10 +1482,7 @@ def validate_meta( "ignore_all": meta.ignore_all, "plugin_data": meta.plugin_data, } - if manager.options.debug_cache: - meta_str = json.dumps(meta_dict, indent=2, sort_keys=True) - else: - meta_str = json.dumps(meta_dict, separators=(",", ":")) + meta_bytes = json_dumps(meta_dict, manager.options.debug_cache) meta_json, _, _ = get_cache_names(id, path, manager.options) manager.log( "Updating mtime for {}: file {}, meta {}, mtime {}".format( @@ -1489,7 +1490,7 @@ def validate_meta( ) ) t1 = time.time() - manager.metastore.write(meta_json, meta_str) # Ignore errors, just an optimization. + manager.metastore.write(meta_json, meta_bytes) # Ignore errors, just an optimization. manager.add_stats(validate_update_time=time.time() - t1, validate_munging_time=t1 - t0) return meta @@ -1507,13 +1508,6 @@ def compute_hash(text: str) -> str: return hash_digest(text.encode("utf-8")) -def json_dumps(obj: Any, debug_cache: bool) -> str: - if debug_cache: - return json.dumps(obj, indent=2, sort_keys=True) - else: - return json.dumps(obj, sort_keys=True, separators=(",", ":")) - - def write_cache( id: str, path: str, @@ -1566,16 +1560,15 @@ def write_cache( # Serialize data and analyze interface data = tree.serialize() - data_str = json_dumps(data, manager.options.debug_cache) - interface_hash = compute_hash(data_str) + data_bytes = json_dumps(data, manager.options.debug_cache) + interface_hash = hash_digest(data_bytes) plugin_data = manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=False)) # Obtain and set up metadata - try: - st = manager.get_stat(path) - except OSError as err: - manager.log(f"Cannot get stat for {path}: {err}") + st = manager.get_stat(path) + if st is None: + manager.log(f"Cannot get stat for {path}") # Remove apparently-invalid cache files. # (This is purely an optimization.) for filename in [data_json, meta_json]: @@ -1592,7 +1585,7 @@ def write_cache( manager.trace(f"Interface for {id} is unchanged") else: manager.trace(f"Interface for {id} has changed") - if not metastore.write(data_json, data_str): + if not metastore.write(data_json, data_bytes): # Most likely the error is the replace() call # (see https://github.com/python/mypy/issues/3215). manager.log(f"Error writing data JSON file {data_json}") @@ -2150,10 +2143,12 @@ def parse_file(self, *, temporary: bool = False) -> None: # other systems, but os.strerror(ioerr.errno) does not, so we use that. # (We want the error messages to be platform-independent so that the # tests have predictable output.) + assert ioerr.errno is not None raise CompileError( [ "mypy: can't read file '{}': {}".format( - self.path, os.strerror(ioerr.errno) + self.path.replace(os.getcwd() + os.sep, ""), + os.strerror(ioerr.errno), ) ], module_with_blocker=self.id, @@ -2668,17 +2663,13 @@ def find_module_and_diagnose( ignore_missing_imports = options.ignore_missing_imports - id_components = id.split(".") # Don't honor a global (not per-module) ignore_missing_imports # setting for modules that used to have bundled stubs, as # otherwise updating mypy can silently result in new false # negatives. (Unless there are stubs but they are incomplete.) global_ignore_missing_imports = manager.options.ignore_missing_imports if ( - any( - ".".join(id_components[:i]) in legacy_bundled_packages - for i in range(len(id_components), 0, -1) - ) + is_module_from_legacy_bundled_package(id) and global_ignore_missing_imports and not options.ignore_missing_imports_per_module and result is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED @@ -2726,7 +2717,9 @@ def exist_added_packages(suppressed: list[str], manager: BuildManager, options: def find_module_simple(id: str, manager: BuildManager) -> str | None: """Find a filesystem path for module `id` or `None` if not found.""" - x = find_module_with_reason(id, manager) + t0 = time.time() + x = manager.find_module_cache.find_module(id, fast_path=True) + manager.add_stats(find_module_time=time.time() - t0, find_module_calls=1) if isinstance(x, ModuleNotFoundReason): return None return x @@ -2735,7 +2728,7 @@ def find_module_simple(id: str, manager: BuildManager) -> str | None: def find_module_with_reason(id: str, manager: BuildManager) -> ModuleSearchResult: """Find a filesystem path for module `id` or the reason it can't be found.""" t0 = time.time() - x = manager.find_module_cache.find_module(id) + x = manager.find_module_cache.find_module(id, fast_path=False) manager.add_stats(find_module_time=time.time() - t0, find_module_calls=1) return x @@ -2797,18 +2790,15 @@ def module_not_found( code = codes.IMPORT errors.report(line, 0, msg.format(module=target), code=code) - components = target.split(".") - for i in range(len(components), 0, -1): - module = ".".join(components[:i]) - if module in legacy_bundled_packages or module in non_bundled_packages: - break - + dist = stub_distribution_name(target) for note in notes: if "{stub_dist}" in note: - note = note.format(stub_dist=stub_distribution_name(module)) + assert dist is not None + note = note.format(stub_dist=dist) errors.report(line, 0, note, severity="note", only_once=True, code=code) if reason is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: - manager.missing_stub_packages.add(stub_distribution_name(module)) + assert dist is not None + manager.missing_stub_packages.add(dist) errors.set_import_context(save_import_context) @@ -2876,7 +2866,7 @@ def log_configuration(manager: BuildManager, sources: list[BuildSource]) -> None manager.log(f"{'Found source:':24}{source}") # Complete list of searched paths can get very long, put them under TRACE - for path_type, paths in manager.search_paths._asdict().items(): + for path_type, paths in manager.search_paths.asdict().items(): if not paths: manager.trace(f"No {path_type}") continue @@ -3543,10 +3533,9 @@ def is_silent_import_module(manager: BuildManager, path: str) -> bool: if manager.options.no_silence_site_packages: return False # Silence errors in site-package dirs and typeshed - return any( - is_sub_path(path, dir) - for dir in manager.search_paths.package_path + manager.search_paths.typeshed_path - ) + if any(is_sub_path_normabs(path, dir) for dir in manager.search_paths.package_path): + return True + return any(is_sub_path_normabs(path, dir) for dir in manager.search_paths.typeshed_path) def write_undocumented_ref_info( @@ -3567,4 +3556,4 @@ def write_undocumented_ref_info( assert not ref_info_file.startswith(".") deps_json = get_undocumented_ref_info_json(state.tree, type_map) - metastore.write(ref_info_file, json.dumps(deps_json, separators=(",", ":"))) + metastore.write(ref_info_file, json_dumps(deps_json)) diff --git a/mypy/checker.py b/mypy/checker.py index 2df74cf7be8dd..2edcaa6bc5c55 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -540,10 +540,11 @@ def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> self.check_top_level(node) else: self.recurse_into_functions = True - if isinstance(node, LambdaExpr): - self.expr_checker.accept(node) - else: - self.accept(node) + with self.binder.top_frame_context(): + if isinstance(node, LambdaExpr): + self.expr_checker.accept(node) + else: + self.accept(node) def check_top_level(self, node: MypyFile) -> None: """Check only the top-level of a module, skipping function definitions.""" @@ -682,11 +683,13 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab inner_type = get_proper_type(inner_type) outer_type: CallableType | None = None if inner_type is not None and not isinstance(inner_type, AnyType): + if isinstance(inner_type, TypeVarLikeType): + inner_type = get_proper_type(inner_type.upper_bound) if isinstance(inner_type, TypeType): - if isinstance(inner_type.item, Instance): - inner_type = expand_type_by_instance( - type_object_type(inner_type.item.type, self.named_type), inner_type.item - ) + inner_type = get_proper_type( + self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) + ) + if isinstance(inner_type, CallableType): outer_type = inner_type elif isinstance(inner_type, Instance): @@ -1057,46 +1060,7 @@ def _visit_func_def(self, defn: FuncDef) -> None: if defn.original_def: # Override previous definition. new_type = self.function_type(defn) - if isinstance(defn.original_def, FuncDef): - # Function definition overrides function definition. - old_type = self.function_type(defn.original_def) - if not is_same_type(new_type, old_type): - self.msg.incompatible_conditional_function_def(defn, old_type, new_type) - else: - # Function definition overrides a variable initialized via assignment or a - # decorated function. - orig_type = defn.original_def.type - if orig_type is None: - # If other branch is unreachable, we don't type check it and so we might - # not have a type for the original definition - return - if isinstance(orig_type, PartialType): - if orig_type.type is None: - # Ah this is a partial type. Give it the type of the function. - orig_def = defn.original_def - if isinstance(orig_def, Decorator): - var = orig_def.var - else: - var = orig_def - partial_types = self.find_partial_types(var) - if partial_types is not None: - var.type = new_type - del partial_types[var] - else: - # Trying to redefine something like partial empty list as function. - self.fail(message_registry.INCOMPATIBLE_REDEFINITION, defn) - else: - name_expr = NameExpr(defn.name) - name_expr.node = defn.original_def - self.binder.assign_type(name_expr, new_type, orig_type) - self.check_subtype( - new_type, - orig_type, - defn, - message_registry.INCOMPATIBLE_REDEFINITION, - "redefinition with type", - "original type", - ) + self.check_func_def_override(defn, new_type) def check_func_item( self, @@ -1132,6 +1096,49 @@ def check_func_item( if dataclasses_plugin.is_processed_dataclass(defn.info): dataclasses_plugin.check_post_init(self, defn, defn.info) + def check_func_def_override(self, defn: FuncDef, new_type: FunctionLike) -> None: + assert defn.original_def is not None + if isinstance(defn.original_def, FuncDef): + # Function definition overrides function definition. + old_type = self.function_type(defn.original_def) + if not is_same_type(new_type, old_type): + self.msg.incompatible_conditional_function_def(defn, old_type, new_type) + else: + # Function definition overrides a variable initialized via assignment or a + # decorated function. + orig_type = defn.original_def.type + if orig_type is None: + # If other branch is unreachable, we don't type check it and so we might + # not have a type for the original definition + return + if isinstance(orig_type, PartialType): + if orig_type.type is None: + # Ah this is a partial type. Give it the type of the function. + orig_def = defn.original_def + if isinstance(orig_def, Decorator): + var = orig_def.var + else: + var = orig_def + partial_types = self.find_partial_types(var) + if partial_types is not None: + var.type = new_type + del partial_types[var] + else: + # Trying to redefine something like partial empty list as function. + self.fail(message_registry.INCOMPATIBLE_REDEFINITION, defn) + else: + name_expr = NameExpr(defn.name) + name_expr.node = defn.original_def + self.binder.assign_type(name_expr, new_type, orig_type) + self.check_subtype( + new_type, + orig_type, + defn, + message_registry.INCOMPATIBLE_REDEFINITION, + "redefinition with type", + "original type", + ) + @contextmanager def enter_attribute_inference_context(self) -> Iterator[None]: old_types = self.inferred_attribute_types @@ -1144,6 +1151,7 @@ def check_func_def( ) -> None: """Type check a function definition.""" # Expand type variables with value restrictions to ordinary types. + self.check_typevar_defaults(typ.variables) expanded = self.expand_typevars(defn, typ) original_typ = typ for item, typ in expanded: @@ -2128,7 +2136,7 @@ def check_method_override_for_base_with_name( pass elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): # Check that the types are compatible. - self.check_override( + ok = self.check_override( typ, original_type, defn.name, @@ -2138,6 +2146,41 @@ def check_method_override_for_base_with_name( override_class_or_static, context, ) + # Check if this override is covariant. + if ( + ok + and original_node + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(original_node) + and not is_subtype(original_type, typ, ignore_pos_arg_names=True) + ): + base_str, override_str = format_type_distinctly( + original_type, typ, options=self.options + ) + msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( + f' (base class "{base.name}" defined the type as {base_str},' + f" override has type {override_str})" + ) + self.fail(msg, context) + elif isinstance(original_type, UnionType) and any( + is_subtype(typ, orig_typ, ignore_pos_arg_names=True) + for orig_typ in original_type.items + ): + # This method is a subtype of at least one union variant. + if ( + original_node + and codes.MUTABLE_OVERRIDE in self.options.enabled_error_codes + and self.is_writable_attribute(original_node) + ): + # Covariant override of mutable attribute. + base_str, override_str = format_type_distinctly( + original_type, typ, options=self.options + ) + msg = message_registry.COVARIANT_OVERRIDE_OF_MUTABLE_ATTRIBUTE.with_additional_msg( + f' (base class "{base.name}" defined the type as {base_str},' + f" override has type {override_str})" + ) + self.fail(msg, context) elif is_equivalent(original_type, typ): # Assume invariance for a non-callable attribute here. Note # that this doesn't affect read-only properties which can have @@ -2227,7 +2270,7 @@ def check_override( original_class_or_static: bool, override_class_or_static: bool, node: Context, - ) -> None: + ) -> bool: """Check a method override with given signatures. Arguments: @@ -2324,10 +2367,11 @@ def erase_override(t: Type) -> Type: else: continue if not is_subtype(original_arg_type, erase_override(override_arg_type)): + context: Context = node if isinstance(node, FuncDef) and not node.is_property: - context: Context = node.arguments[i + len(override.bound_args)] - else: - context = node + arg_node = node.arguments[i + len(override.bound_args)] + if arg_node.line != -1: + context = arg_node self.msg.argument_incompatible_with_supertype( i + 1, name, @@ -2377,6 +2421,7 @@ def erase_override(t: Type) -> Type: node, code=codes.OVERRIDE, ) + return not fail def check__exit__return_type(self, defn: FuncItem) -> None: """Generate error if the return type of __exit__ is problematic. @@ -2468,6 +2513,8 @@ def visit_class_def(self, defn: ClassDef) -> None: context=defn, code=codes.TYPE_VAR, ) + if typ.defn.type_vars: + self.check_typevar_defaults(typ.defn.type_vars) if typ.is_protocol and typ.defn.type_vars: self.check_protocol_variance(defn) @@ -2531,22 +2578,41 @@ def check_init_subclass(self, defn: ClassDef) -> None: # all other bases have already been checked. break + def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None: + for tv in tvars: + if not (isinstance(tv, TypeVarType) and tv.has_default()): + continue + if not is_subtype(tv.default, tv.upper_bound): + self.fail("TypeVar default must be a subtype of the bound type", tv) + if tv.values and not any(tv.default == value for value in tv.values): + self.fail("TypeVar default must be one of the constraint types", tv) + def check_enum(self, defn: ClassDef) -> None: assert defn.info.is_enum - if defn.info.fullname not in ENUM_BASES: - for sym in defn.info.names.values(): - if ( - isinstance(sym.node, Var) - and sym.node.has_explicit_value - and sym.node.name == "__members__" - ): - # `__members__` will always be overwritten by `Enum` and is considered - # read-only so we disallow assigning a value to it - self.fail(message_registry.ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN, sym.node) + if defn.info.fullname not in ENUM_BASES and "__members__" in defn.info.names: + sym = defn.info.names["__members__"] + if isinstance(sym.node, Var) and sym.node.has_explicit_value: + # `__members__` will always be overwritten by `Enum` and is considered + # read-only so we disallow assigning a value to it + self.fail(message_registry.ENUM_MEMBERS_ATTR_WILL_BE_OVERRIDEN, sym.node) for base in defn.info.mro[1:-1]: # we don't need self and `object` if base.is_enum and base.fullname not in ENUM_BASES: self.check_final_enum(defn, base) + if self.is_stub and self.tree.fullname not in {"enum", "_typeshed"}: + if not defn.info.enum_members: + self.fail( + f'Detected enum "{defn.info.fullname}" in a type stub with zero members. ' + "There is a chance this is due to a recent change in the semantics of " + "enum membership. If so, use `member = value` to mark an enum member, " + "instead of `member: type`", + defn, + ) + self.note( + "See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members", + defn, + ) + self.check_enum_bases(defn) self.check_enum_new(defn) @@ -2835,6 +2901,9 @@ def check_metaclass_compatibility(self, typ: TypeInfo) -> None: ) def visit_import_from(self, node: ImportFrom) -> None: + for name, _ in node.names: + if (sym := self.globals.get(name)) is not None: + self.warn_deprecated(sym.node, node) self.check_import(node) def visit_import_all(self, node: ImportAll) -> None: @@ -2923,6 +2992,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: Handle all kinds of assignment statements (simple, indexed, multiple). """ + # Avoid type checking type aliases in stubs to avoid false # positives about modern type syntax available in stubs such # as X | Y. @@ -2971,7 +3041,8 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.msg.annotation_in_unchecked_function(context=s) def check_type_alias_rvalue(self, s: AssignmentStmt) -> None: - alias_type = self.expr_checker.accept(s.rvalue) + with self.msg.filter_errors(): + alias_type = self.expr_checker.accept(s.rvalue) self.store_type(s.lvalues[-1], alias_type) def check_assignment( @@ -3130,6 +3201,14 @@ def check_assignment( # Don't use type binder for definitions of special forms, like named tuples. if not (isinstance(lvalue, NameExpr) and lvalue.is_special_form): self.binder.assign_type(lvalue, rvalue_type, lvalue_type, False) + if ( + isinstance(lvalue, NameExpr) + and isinstance(lvalue.node, Var) + and lvalue.node.is_inferred + and lvalue.node.is_index_var + and lvalue_type is not None + ): + lvalue.node.type = remove_instance_last_known_values(lvalue_type) elif index_lvalue: self.check_indexed_assignment(index_lvalue, rvalue, lvalue) @@ -3139,6 +3218,7 @@ def check_assignment( rvalue_type = self.expr_checker.accept(rvalue, type_context=type_context) if not ( inferred.is_final + or inferred.is_index_var or (isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__") ): rvalue_type = remove_instance_last_known_values(rvalue_type) @@ -4346,7 +4426,7 @@ def check_member_assignment( msg=self.msg, chk=self, ) - get_type = analyze_descriptor_access(attribute_type, mx) + get_type = analyze_descriptor_access(attribute_type, mx, assignment=True) if not attribute_type.type.has_readable_member("__set__"): # If there is no __set__, we type-check that the assigned value matches # the return type of __get__. This doesn't match the python semantics, @@ -4413,6 +4493,12 @@ def check_member_assignment( callable_name=callable_name, ) + # Search for possible deprecations: + mx.chk.check_deprecated(dunder_set, mx.context) + mx.chk.warn_deprecated_overload_item( + dunder_set, mx.context, target=inferred_dunder_set_type, selftype=attribute_type + ) + # In the following cases, a message already will have been recorded in check_call. if (not isinstance(inferred_dunder_set_type, CallableType)) or ( len(inferred_dunder_set_type.arg_types) < 2 @@ -4564,6 +4650,13 @@ def check_return_stmt(self, s: ReturnStmt) -> None: s.expr, return_type, allow_none_return=allow_none_func_call ) ) + # Treat NotImplemented as having type Any, consistent with its + # definition in typeshed prior to python/typeshed#4222. + if ( + isinstance(typ, Instance) + and typ.type.fullname == "builtins._NotImplementedType" + ): + typ = AnyType(TypeOfAny.special_form) if defn.is_async_generator: self.fail(message_registry.RETURN_IN_ASYNC_GENERATOR, s) @@ -4638,11 +4731,11 @@ def visit_if_stmt(self, s: IfStmt) -> None: # XXX Issue a warning if condition is always False? with self.binder.frame_context(can_skip=True, fall_through=2): - self.push_type_map(if_map) + self.push_type_map(if_map, from_assignment=False) self.accept(b) # XXX Issue a warning if condition is always True? - self.push_type_map(else_map) + self.push_type_map(else_map, from_assignment=False) with self.binder.frame_context(can_skip=False, fall_through=2): if s.else_body: @@ -4667,6 +4760,16 @@ def visit_operator_assignment_stmt(self, s: OperatorAssignmentStmt) -> None: if inplace: # There is __ifoo__, treat as x = x.__ifoo__(y) rvalue_type, method_type = self.expr_checker.check_op(method, lvalue_type, s.rvalue, s) + if isinstance(inst := get_proper_type(lvalue_type), Instance) and isinstance( + defn := inst.type.get_method(method), OverloadedFuncDef + ): + for item in defn.items: + if ( + isinstance(item, Decorator) + and isinstance(typ := item.func.type, CallableType) + and (bind_self(typ) == method_type) + ): + self.warn_deprecated(item.func, s) if not is_subtype(rvalue_type, lvalue_type): self.msg.incompatible_operator_assignment(s.op, s) else: @@ -4719,6 +4822,14 @@ def type_check_raise(self, e: Expression, s: RaiseStmt, optional: bool = False) # https://github.com/python/mypy/issues/11089 self.expr_checker.check_call(typ, [], [], e) + if isinstance(typ, Instance) and typ.type.fullname == "builtins._NotImplementedType": + self.fail( + message_registry.INVALID_EXCEPTION.with_additional_msg( + '; did you mean "NotImplementedError"?' + ), + s, + ) + def visit_try_stmt(self, s: TryStmt) -> None: """Type check a try statement.""" # Our enclosing frame will get the result if the try/except falls through. @@ -5054,6 +5165,10 @@ def visit_decorator_inner(self, e: Decorator, allow_empty: bool = False) -> None if e.type and not isinstance(get_proper_type(e.type), (FunctionLike, AnyType)): self.fail(message_registry.BAD_CONSTRUCTOR_TYPE, e) + if e.func.original_def and isinstance(sig, FunctionLike): + # Function definition overrides function definition. + self.check_func_def_override(e.func, sig) + def check_for_untyped_decorator( self, func: FuncDef, dec_type: Type, dec_expr: Expression ) -> None: @@ -5201,18 +5316,21 @@ def visit_match_stmt(self, s: MatchStmt) -> None: if b.is_unreachable or isinstance( get_proper_type(pattern_type.type), UninhabitedType ): - self.push_type_map(None) + self.push_type_map(None, from_assignment=False) else_map: TypeMap = {} else: pattern_map, else_map = conditional_types_to_typemaps( named_subject, pattern_type.type, pattern_type.rest_type ) self.remove_capture_conflicts(pattern_type.captures, inferred_types) - self.push_type_map(pattern_map) + self.push_type_map(pattern_map, from_assignment=False) if pattern_map: for expr, typ in pattern_map.items(): - self.push_type_map(self._get_recursive_sub_patterns_map(expr, typ)) - self.push_type_map(pattern_type.captures) + self.push_type_map( + self._get_recursive_sub_patterns_map(expr, typ), + from_assignment=False, + ) + self.push_type_map(pattern_type.captures, from_assignment=False) if g is not None: with self.binder.frame_context(can_skip=False, fall_through=3): gt = get_proper_type(self.expr_checker.accept(g)) @@ -5238,11 +5356,11 @@ def visit_match_stmt(self, s: MatchStmt) -> None: continue type_map[named_subject] = type_map[expr] - self.push_type_map(guard_map) + self.push_type_map(guard_map, from_assignment=False) self.accept(b) else: self.accept(b) - self.push_type_map(else_map) + self.push_type_map(else_map, from_assignment=False) # This is needed due to a quirk in frame_context. Without it types will stay narrowed # after the match. @@ -5311,7 +5429,11 @@ def remove_capture_conflicts(self, type_map: TypeMap, inferred_types: dict[Var, del type_map[expr] def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: - self.expr_checker.accept(o.value) + if o.alias_node: + self.check_typevar_defaults(o.alias_node.alias_tvars) + + with self.msg.filter_errors(): + self.expr_checker.accept(o.value) def make_fake_typeinfo( self, @@ -5520,7 +5642,7 @@ def partition_by_callable( if isinstance(typ, TypeVarType): # We could do better probably? - # Refine the the type variable's bound as our type in the case that + # Refine the type variable's bound as our type in the case that # callable() is true. This unfortunately loses the information that # the type is a type variable in that branch. # This matches what is done for isinstance, but it may be possible to @@ -5646,7 +5768,16 @@ def _is_truthy_type(self, t: ProperType) -> bool: ) ) - def _check_for_truthy_type(self, t: Type, expr: Expression) -> None: + def check_for_truthy_type(self, t: Type, expr: Expression) -> None: + """ + Check if a type can have a truthy value. + + Used in checks like:: + + if x: # <--- + + not x # <--- + """ if not state.strict_optional: return # if everything can be None, all bets are off @@ -5887,177 +6018,10 @@ def find_isinstance_check_helper( ), ) elif isinstance(node, ComparisonExpr): - # Step 1: Obtain the types of each operand and whether or not we can - # narrow their types. (For example, we shouldn't try narrowing the - # types of literal string or enum expressions). - - operands = [collapse_walrus(x) for x in node.operands] - operand_types = [] - narrowable_operand_index_to_hash = {} - for i, expr in enumerate(operands): - if not self.has_type(expr): - return {}, {} - expr_type = self.lookup_type(expr) - operand_types.append(expr_type) - - if ( - literal(expr) == LITERAL_TYPE - and not is_literal_none(expr) - and not self.is_literal_enum(expr) - ): - h = literal_hash(expr) - if h is not None: - narrowable_operand_index_to_hash[i] = h - - # Step 2: Group operands chained by either the 'is' or '==' operands - # together. For all other operands, we keep them in groups of size 2. - # So the expression: - # - # x0 == x1 == x2 < x3 < x4 is x5 is x6 is not x7 is not x8 - # - # ...is converted into the simplified operator list: - # - # [("==", [0, 1, 2]), ("<", [2, 3]), ("<", [3, 4]), - # ("is", [4, 5, 6]), ("is not", [6, 7]), ("is not", [7, 8])] - # - # We group identity/equality expressions so we can propagate information - # we discover about one operand across the entire chain. We don't bother - # handling 'is not' and '!=' chains in a special way: those are very rare - # in practice. - - simplified_operator_list = group_comparison_operands( - node.pairwise(), narrowable_operand_index_to_hash, {"==", "is"} - ) - - # Step 3: Analyze each group and infer more precise type maps for each - # assignable operand, if possible. We combine these type maps together - # in the final step. - - partial_type_maps = [] - for operator, expr_indices in simplified_operator_list: - if operator in {"is", "is not", "==", "!="}: - # is_valid_target: - # Controls which types we're allowed to narrow exprs to. Note that - # we cannot use 'is_literal_type_like' in both cases since doing - # 'x = 10000 + 1; x is 10001' is not always True in all Python - # implementations. - # - # coerce_only_in_literal_context: - # If true, coerce types into literal types only if one or more of - # the provided exprs contains an explicit Literal type. This could - # technically be set to any arbitrary value, but it seems being liberal - # with narrowing when using 'is' and conservative when using '==' seems - # to break the least amount of real-world code. - # - # should_narrow_by_identity: - # Set to 'false' only if the user defines custom __eq__ or __ne__ methods - # that could cause identity-based narrowing to produce invalid results. - if operator in {"is", "is not"}: - is_valid_target: Callable[[Type], bool] = is_singleton_type - coerce_only_in_literal_context = False - should_narrow_by_identity = True - else: - - def is_exactly_literal_type(t: Type) -> bool: - return isinstance(get_proper_type(t), LiteralType) - - def has_no_custom_eq_checks(t: Type) -> bool: - return not custom_special_method( - t, "__eq__", check_all=False - ) and not custom_special_method(t, "__ne__", check_all=False) - - is_valid_target = is_exactly_literal_type - coerce_only_in_literal_context = True - - expr_types = [operand_types[i] for i in expr_indices] - should_narrow_by_identity = all(map(has_no_custom_eq_checks, expr_types)) - - if_map: TypeMap = {} - else_map: TypeMap = {} - if should_narrow_by_identity: - if_map, else_map = self.refine_identity_comparison_expression( - operands, - operand_types, - expr_indices, - narrowable_operand_index_to_hash.keys(), - is_valid_target, - coerce_only_in_literal_context, - ) - - # Strictly speaking, we should also skip this check if the objects in the expr - # chain have custom __eq__ or __ne__ methods. But we (maybe optimistically) - # assume nobody would actually create a custom objects that considers itself - # equal to None. - if if_map == {} and else_map == {}: - if_map, else_map = self.refine_away_none_in_comparison( - operands, - operand_types, - expr_indices, - narrowable_operand_index_to_hash.keys(), - ) - - # If we haven't been able to narrow types yet, we might be dealing with a - # explicit type(x) == some_type check - if if_map == {} and else_map == {}: - if_map, else_map = self.find_type_equals_check(node, expr_indices) - elif operator in {"in", "not in"}: - assert len(expr_indices) == 2 - left_index, right_index = expr_indices - item_type = operand_types[left_index] - iterable_type = operand_types[right_index] - - if_map, else_map = {}, {} - - if left_index in narrowable_operand_index_to_hash: - # We only try and narrow away 'None' for now - if is_overlapping_none(item_type): - collection_item_type = get_proper_type( - builtin_item_type(iterable_type) - ) - if ( - collection_item_type is not None - and not is_overlapping_none(collection_item_type) - and not ( - isinstance(collection_item_type, Instance) - and collection_item_type.type.fullname == "builtins.object" - ) - and is_overlapping_erased_types(item_type, collection_item_type) - ): - if_map[operands[left_index]] = remove_optional(item_type) - - if right_index in narrowable_operand_index_to_hash: - if_type, else_type = self.conditional_types_for_iterable( - item_type, iterable_type - ) - expr = operands[right_index] - if if_type is None: - if_map = None - else: - if_map[expr] = if_type - if else_type is None: - else_map = None - else: - else_map[expr] = else_type - - else: - if_map = {} - else_map = {} - - if operator in {"is not", "!=", "not in"}: - if_map, else_map = else_map, if_map - - partial_type_maps.append((if_map, else_map)) - - # If we have found non-trivial restrictions from the regular comparisons, - # then return soon. Otherwise try to infer restrictions involving `len(x)`. - # TODO: support regular and len() narrowing in the same chain. - if any(m != ({}, {}) for m in partial_type_maps): - return reduce_conditional_maps(partial_type_maps) - else: - # Use meet for `and` maps to get correct results for chained checks - # like `if 1 < len(x) < 4: ...` - return reduce_conditional_maps(self.find_tuple_len_narrowing(node), use_meet=True) + return self.comparison_type_narrowing_helper(node) elif isinstance(node, AssignmentExpr): + if_map: dict[Expression, Type] | None + else_map: dict[Expression, Type] | None if_map = {} else_map = {} @@ -6135,7 +6099,7 @@ def has_no_custom_eq_checks(t: Type) -> bool: if in_boolean_context: # We don't check `:=` values in expressions like `(a := A())`, # because they produce two error messages. - self._check_for_truthy_type(original_vartype, node) + self.check_for_truthy_type(original_vartype, node) vartype = try_expanding_sum_type_to_union(original_vartype, "builtins.bool") if_type = true_only(vartype) @@ -6144,6 +6108,192 @@ def has_no_custom_eq_checks(t: Type) -> bool: else_map = {node: else_type} if not isinstance(else_type, UninhabitedType) else None return if_map, else_map + def comparison_type_narrowing_helper(self, node: ComparisonExpr) -> tuple[TypeMap, TypeMap]: + """Infer type narrowing from a comparison expression.""" + # Step 1: Obtain the types of each operand and whether or not we can + # narrow their types. (For example, we shouldn't try narrowing the + # types of literal string or enum expressions). + + operands = [collapse_walrus(x) for x in node.operands] + operand_types = [] + narrowable_operand_index_to_hash = {} + for i, expr in enumerate(operands): + if not self.has_type(expr): + return {}, {} + expr_type = self.lookup_type(expr) + operand_types.append(expr_type) + + if ( + literal(expr) == LITERAL_TYPE + and not is_literal_none(expr) + and not self.is_literal_enum(expr) + ): + h = literal_hash(expr) + if h is not None: + narrowable_operand_index_to_hash[i] = h + + # Step 2: Group operands chained by either the 'is' or '==' operands + # together. For all other operands, we keep them in groups of size 2. + # So the expression: + # + # x0 == x1 == x2 < x3 < x4 is x5 is x6 is not x7 is not x8 + # + # ...is converted into the simplified operator list: + # + # [("==", [0, 1, 2]), ("<", [2, 3]), ("<", [3, 4]), + # ("is", [4, 5, 6]), ("is not", [6, 7]), ("is not", [7, 8])] + # + # We group identity/equality expressions so we can propagate information + # we discover about one operand across the entire chain. We don't bother + # handling 'is not' and '!=' chains in a special way: those are very rare + # in practice. + + simplified_operator_list = group_comparison_operands( + node.pairwise(), narrowable_operand_index_to_hash, {"==", "is"} + ) + + # Step 3: Analyze each group and infer more precise type maps for each + # assignable operand, if possible. We combine these type maps together + # in the final step. + + partial_type_maps = [] + for operator, expr_indices in simplified_operator_list: + if operator in {"is", "is not", "==", "!="}: + if_map, else_map = self.equality_type_narrowing_helper( + node, + operator, + operands, + operand_types, + expr_indices, + narrowable_operand_index_to_hash, + ) + elif operator in {"in", "not in"}: + assert len(expr_indices) == 2 + left_index, right_index = expr_indices + item_type = operand_types[left_index] + iterable_type = operand_types[right_index] + + if_map, else_map = {}, {} + + if left_index in narrowable_operand_index_to_hash: + # We only try and narrow away 'None' for now + if is_overlapping_none(item_type): + collection_item_type = get_proper_type(builtin_item_type(iterable_type)) + if ( + collection_item_type is not None + and not is_overlapping_none(collection_item_type) + and not ( + isinstance(collection_item_type, Instance) + and collection_item_type.type.fullname == "builtins.object" + ) + and is_overlapping_erased_types(item_type, collection_item_type) + ): + if_map[operands[left_index]] = remove_optional(item_type) + + if right_index in narrowable_operand_index_to_hash: + if_type, else_type = self.conditional_types_for_iterable( + item_type, iterable_type + ) + expr = operands[right_index] + if if_type is None: + if_map = None + else: + if_map[expr] = if_type + if else_type is None: + else_map = None + else: + else_map[expr] = else_type + + else: + if_map = {} + else_map = {} + + if operator in {"is not", "!=", "not in"}: + if_map, else_map = else_map, if_map + + partial_type_maps.append((if_map, else_map)) + + # If we have found non-trivial restrictions from the regular comparisons, + # then return soon. Otherwise try to infer restrictions involving `len(x)`. + # TODO: support regular and len() narrowing in the same chain. + if any(m != ({}, {}) for m in partial_type_maps): + return reduce_conditional_maps(partial_type_maps) + else: + # Use meet for `and` maps to get correct results for chained checks + # like `if 1 < len(x) < 4: ...` + return reduce_conditional_maps(self.find_tuple_len_narrowing(node), use_meet=True) + + def equality_type_narrowing_helper( + self, + node: ComparisonExpr, + operator: str, + operands: list[Expression], + operand_types: list[Type], + expr_indices: list[int], + narrowable_operand_index_to_hash: dict[int, tuple[Key, ...]], + ) -> tuple[TypeMap, TypeMap]: + """Calculate type maps for '==', '!=', 'is' or 'is not' expression.""" + # is_valid_target: + # Controls which types we're allowed to narrow exprs to. Note that + # we cannot use 'is_literal_type_like' in both cases since doing + # 'x = 10000 + 1; x is 10001' is not always True in all Python + # implementations. + # + # coerce_only_in_literal_context: + # If true, coerce types into literal types only if one or more of + # the provided exprs contains an explicit Literal type. This could + # technically be set to any arbitrary value, but it seems being liberal + # with narrowing when using 'is' and conservative when using '==' seems + # to break the least amount of real-world code. + # + # should_narrow_by_identity: + # Set to 'false' only if the user defines custom __eq__ or __ne__ methods + # that could cause identity-based narrowing to produce invalid results. + if operator in {"is", "is not"}: + is_valid_target: Callable[[Type], bool] = is_singleton_type + coerce_only_in_literal_context = False + should_narrow_by_identity = True + else: + + def is_exactly_literal_type(t: Type) -> bool: + return isinstance(get_proper_type(t), LiteralType) + + def has_no_custom_eq_checks(t: Type) -> bool: + return not custom_special_method( + t, "__eq__", check_all=False + ) and not custom_special_method(t, "__ne__", check_all=False) + + is_valid_target = is_exactly_literal_type + coerce_only_in_literal_context = True + + expr_types = [operand_types[i] for i in expr_indices] + should_narrow_by_identity = all( + map(has_no_custom_eq_checks, expr_types) + ) and not is_ambiguous_mix_of_enums(expr_types) + + if_map: TypeMap = {} + else_map: TypeMap = {} + if should_narrow_by_identity: + if_map, else_map = self.refine_identity_comparison_expression( + operands, + operand_types, + expr_indices, + narrowable_operand_index_to_hash.keys(), + is_valid_target, + coerce_only_in_literal_context, + ) + + if if_map == {} and else_map == {}: + if_map, else_map = self.refine_away_none_in_comparison( + operands, operand_types, expr_indices, narrowable_operand_index_to_hash.keys() + ) + + # If we haven't been able to narrow types yet, we might be dealing with a + # explicit type(x) == some_type check + if if_map == {} and else_map == {}: + if_map, else_map = self.find_type_equals_check(node, expr_indices) + return if_map, else_map + def propagate_up_typemap_info(self, new_types: TypeMap) -> TypeMap: """Attempts refining parent expressions of any MemberExpr or IndexExprs in new_types. @@ -6457,25 +6607,36 @@ def refine_away_none_in_comparison( For more details about what the different arguments mean, see the docstring of 'refine_identity_comparison_expression' up above. """ + non_optional_types = [] for i in chain_indices: typ = operand_types[i] if not is_overlapping_none(typ): non_optional_types.append(typ) - # Make sure we have a mixture of optional and non-optional types. - if len(non_optional_types) == 0 or len(non_optional_types) == len(chain_indices): - return {}, {} + if_map, else_map = {}, {} - if_map = {} - for i in narrowable_operand_indices: - expr_type = operand_types[i] - if not is_overlapping_none(expr_type): - continue - if any(is_overlapping_erased_types(expr_type, t) for t in non_optional_types): - if_map[operands[i]] = remove_optional(expr_type) + if not non_optional_types or (len(non_optional_types) != len(chain_indices)): - return if_map, {} + # Narrow e.g. `Optional[A] == "x"` or `Optional[A] is "x"` to `A` (which may be + # convenient but is strictly not type-safe): + for i in narrowable_operand_indices: + expr_type = operand_types[i] + if not is_overlapping_none(expr_type): + continue + if any(is_overlapping_erased_types(expr_type, t) for t in non_optional_types): + if_map[operands[i]] = remove_optional(expr_type) + + # Narrow e.g. `Optional[A] != None` to `A` (which is stricter than the above step and + # so type-safe but less convenient, because e.g. `Optional[A] == None` still results + # in `Optional[A]`): + if any(isinstance(get_proper_type(ot), NoneType) for ot in operand_types): + for i in narrowable_operand_indices: + expr_type = operand_types[i] + if is_overlapping_none(expr_type): + else_map[operands[i]] = remove_optional(expr_type) + + return if_map, else_map def is_len_of_tuple(self, expr: Expression) -> bool: """Is this expression a `len(x)` call where x is a tuple or union of tuples?""" @@ -7220,12 +7381,12 @@ def iterable_item_type( def function_type(self, func: FuncBase) -> FunctionLike: return function_type(func, self.named_type("builtins.function")) - def push_type_map(self, type_map: TypeMap) -> None: + def push_type_map(self, type_map: TypeMap, *, from_assignment: bool = True) -> None: if type_map is None: self.binder.unreachable() else: for expr, type in type_map.items(): - self.binder.put(expr, type) + self.binder.put(expr, type, from_assignment=from_assignment) def infer_issubclass_maps(self, node: CallExpr, expr: Expression) -> tuple[TypeMap, TypeMap]: """Infer type restrictions for an expression in issubclass call.""" @@ -7504,10 +7665,10 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: name, typ, TempNode(AnyType(TypeOfAny.special_form)), - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=typ, chk=self, # This is not a real attribute lookup so don't mess with deferring nodes. @@ -7519,6 +7680,46 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool: def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type: return self.expr_checker.accept(node, type_context=type_context) + def check_deprecated(self, node: Node | None, context: Context) -> None: + """Warn if deprecated and not directly imported with a `from` statement.""" + if isinstance(node, Decorator): + node = node.func + if isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo)) and ( + node.deprecated is not None + ): + for imp in self.tree.imports: + if isinstance(imp, ImportFrom) and any(node.name == n[0] for n in imp.names): + break + else: + self.warn_deprecated(node, context) + + def warn_deprecated(self, node: Node | None, context: Context) -> None: + """Warn if deprecated.""" + if isinstance(node, Decorator): + node = node.func + if ( + isinstance(node, (FuncDef, OverloadedFuncDef, TypeInfo)) + and ((deprecated := node.deprecated) is not None) + and not self.is_typeshed_stub + ): + warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail + warn(deprecated, context, code=codes.DEPRECATED) + + def warn_deprecated_overload_item( + self, node: Node | None, context: Context, *, target: Type, selftype: Type | None = None + ) -> None: + """Warn if the overload item corresponding to the given callable is deprecated.""" + target = get_proper_type(target) + if isinstance(node, OverloadedFuncDef) and isinstance(target, CallableType): + for item in node.items: + if isinstance(item, Decorator) and isinstance( + candidate := item.func.type, CallableType + ): + if selftype is not None: + candidate = bind_self(candidate, selftype) + if candidate == target: + self.warn_deprecated(item.func, context) + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" @@ -7573,9 +7774,7 @@ def conditional_types( ) and is_proper_subtype(current_type, proposed_type, ignore_promotions=True): # Expression is always of one of the types in proposed_type_ranges return default, UninhabitedType() - elif not is_overlapping_types( - current_type, proposed_type, prohibit_none_typevar_overlap=True, ignore_promotions=True - ): + elif not is_overlapping_types(current_type, proposed_type, ignore_promotions=True): # Expression is never of any type in proposed_type_ranges return UninhabitedType(), default else: @@ -8590,3 +8789,47 @@ def visit_starred_pattern(self, p: StarredPattern) -> None: self.lvalue = True p.capture.accept(self) self.lvalue = False + + +def is_ambiguous_mix_of_enums(types: list[Type]) -> bool: + """Do types have IntEnum/StrEnum types that are potentially overlapping with other types? + + If True, we shouldn't attempt type narrowing based on enum values, as it gets + too ambiguous. + + For example, return True if there's an 'int' type together with an IntEnum literal. + However, IntEnum together with a literal of the same IntEnum type is not ambiguous. + """ + # We need these things for this to be ambiguous: + # (1) an IntEnum or StrEnum type + # (2) either a different IntEnum/StrEnum type or a non-enum type ("") + # + # It would be slightly more correct to calculate this separately for IntEnum and + # StrEnum related types, as an IntEnum can't be confused with a StrEnum. + return len(_ambiguous_enum_variants(types)) > 1 + + +def _ambiguous_enum_variants(types: list[Type]) -> set[str]: + result = set() + for t in types: + t = get_proper_type(t) + if isinstance(t, UnionType): + result.update(_ambiguous_enum_variants(t.items)) + elif isinstance(t, Instance): + if t.last_known_value: + result.update(_ambiguous_enum_variants([t.last_known_value])) + elif t.type.is_enum and any( + base.fullname in ("enum.IntEnum", "enum.StrEnum") for base in t.type.mro + ): + result.add(t.type.fullname) + elif not t.type.is_enum: + # These might compare equal to IntEnum/StrEnum types (e.g. Decimal), so + # let's be conservative + result.add("") + elif isinstance(t, LiteralType): + result.update(_ambiguous_enum_variants([t.fallback])) + elif isinstance(t, NoneType): + pass + else: + result.add("") + return result diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index fdc0f94b39970..34004b5e3b486 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -126,6 +126,7 @@ validate_instance, ) from mypy.typeops import ( + bind_self, callable_type, custom_special_method, erase_to_union_or_bound, @@ -180,6 +181,7 @@ get_proper_type, get_proper_types, has_recursive_types, + has_type_vars, is_named_instance, split_with_prefix_and_suffix, ) @@ -354,7 +356,9 @@ def visit_name_expr(self, e: NameExpr) -> Type: """ self.chk.module_refs.update(extract_refexpr_names(e)) result = self.analyze_ref_expr(e) - return self.narrow_type_from_binder(e, result) + narrowed = self.narrow_type_from_binder(e, result) + self.chk.check_deprecated(e.node, e) + return narrowed def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result: Type | None = None @@ -396,8 +400,8 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: # TODO: always do this in type_object_type by passing the original context result.ret_type.line = e.line result.ret_type.column = e.column - if isinstance(get_proper_type(self.type_context[-1]), TypeType): - # This is the type in a Type[] expression, so substitute type + if is_type_type_context(self.type_context[-1]): + # This is the type in a type[] expression, so substitute type # variables with Any. result = erasetype.erase_typevars(result) elif isinstance(node, MypyFile): @@ -986,6 +990,10 @@ def check_typeddict_call_with_kwargs( always_present_keys: set[str], ) -> Type: actual_keys = kwargs.keys() + if callee.to_be_mutated: + assigned_readonly_keys = actual_keys & callee.readonly_keys + if assigned_readonly_keys: + self.msg.readonly_keys_mutated(assigned_readonly_keys, context=context) if not ( callee.required_keys <= always_present_keys and actual_keys <= callee.items.keys() ): @@ -1475,6 +1483,8 @@ def check_call_expr_with_callee_type( object_type=object_type, ) proper_callee = get_proper_type(callee_type) + if isinstance(e.callee, (NameExpr, MemberExpr)): + self.chk.warn_deprecated_overload_item(e.callee.node, e, target=callee_type) if isinstance(e.callee, RefExpr) and isinstance(proper_callee, CallableType): # Cache it for find_isinstance_check() if proper_callee.type_guard is not None: @@ -1493,10 +1503,10 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str member, typ, e, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=object_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -1756,7 +1766,11 @@ def check_callable_call( ) param_spec = callee.param_spec() - if param_spec is not None and arg_kinds == [ARG_STAR, ARG_STAR2]: + if ( + param_spec is not None + and arg_kinds == [ARG_STAR, ARG_STAR2] + and len(formal_to_actual) == 2 + ): arg1 = self.accept(args[0]) arg2 = self.accept(args[1]) if ( @@ -2362,6 +2376,13 @@ def check_argument_count( # Positional argument when expecting a keyword argument. self.msg.too_many_positional_arguments(callee, context) ok = False + elif ( + callee.param_spec() is not None + and not formal_to_actual[i] + and callee.special_sig != "partial" + ): + self.msg.too_few_arguments(callee, context, actual_names) + ok = False return ok def check_for_extra_actual_arguments( @@ -2459,6 +2480,17 @@ def check_argument_types( check_arg = check_arg or self.check_arg # Keep track of consumed tuple *arg items. mapper = ArgTypeExpander(self.argument_infer_context()) + + for arg_type, arg_kind in zip(arg_types, arg_kinds): + arg_type = get_proper_type(arg_type) + if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type): + self.msg.invalid_var_arg(arg_type, context) + if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(arg_type): + is_mapping = is_subtype( + arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") + ) + self.msg.invalid_keyword_var_arg(arg_type, is_mapping, context) + for i, actuals in enumerate(formal_to_actual): orig_callee_arg_type = get_proper_type(callee.arg_types[i]) @@ -2551,15 +2583,6 @@ def check_argument_types( if actual_type is None: continue # Some kind of error was already reported. # Check that a *arg is valid as varargs. - if actual_kind == nodes.ARG_STAR and not self.is_valid_var_arg(actual_type): - self.msg.invalid_var_arg(actual_type, context) - if actual_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg( - actual_type - ): - is_mapping = is_subtype( - actual_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") - ) - self.msg.invalid_keyword_var_arg(actual_type, is_mapping, context) expanded_actual = mapper.expand_actual_type( actual_type, actual_kind, @@ -2763,9 +2786,9 @@ def plausible_overload_call_targets( ) -> list[CallableType]: """Returns all overload call targets that having matching argument counts. - If the given args contains a star-arg (*arg or **kwarg argument), this method - will ensure all star-arg overloads appear at the start of the list, instead - of their usual location. + If the given args contains a star-arg (*arg or **kwarg argument, except for + ParamSpec), this method will ensure all star-arg overloads appear at the start + of the list, instead of their usual location. The only exception is if the starred argument is something like a Tuple or a NamedTuple, which has a definitive "shape". If so, we don't move the corresponding @@ -2793,9 +2816,15 @@ def has_shape(typ: Type) -> bool: formal_to_actual = map_actuals_to_formals( arg_kinds, arg_names, typ.arg_kinds, typ.arg_names, lambda i: arg_types[i] ) - with self.msg.filter_errors(): - if self.check_argument_count( + if typ.param_spec() is not None: + # ParamSpec can be expanded in a lot of different ways. We may try + # to expand it here instead, but picking an impossible overload + # is safe: it will be filtered out later. + # Unlike other var-args signatures, ParamSpec produces essentially + # a fixed signature, so there's no need to push them to the top. + matches.append(typ) + elif self.check_argument_count( typ, arg_types, arg_kinds, arg_names, formal_to_actual, None ): if args_have_var_arg and typ.is_var_arg: @@ -3252,7 +3281,9 @@ def visit_member_expr(self, e: MemberExpr, is_lvalue: bool = False) -> Type: """Visit member expression (of form e.id).""" self.chk.module_refs.update(extract_refexpr_names(e)) result = self.analyze_ordinary_member_access(e, is_lvalue) - return self.narrow_type_from_binder(e, result) + narrowed = self.narrow_type_from_binder(e, result) + self.chk.warn_deprecated(e.node, e) + return narrowed def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type: """Analyse member expression or member lvalue.""" @@ -3278,10 +3309,10 @@ def analyze_ordinary_member_access(self, e: MemberExpr, is_lvalue: bool) -> Type e.name, original_type, e, - is_lvalue, - False, - False, - self.msg, + is_lvalue=is_lvalue, + is_super=False, + is_operator=False, + msg=self.msg, original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3302,10 +3333,10 @@ def analyze_external_member_access( member, base_type, context, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -3785,11 +3816,12 @@ def check_method_call_by_name( method, base_type, context, - False, - False, - True, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=True, + msg=self.msg, original_type=original_type, + self_type=base_type, chk=self.chk, in_literal_context=self.is_literal_context(), ) @@ -3941,7 +3973,7 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: # This is the case even if the __add__ method is completely missing and the __radd__ # method is defined. - variants_raw = [(left_op, left_type, right_expr)] + variants_raw = [(op_name, left_op, left_type, right_expr)] elif ( is_subtype(right_type, left_type) and isinstance(left_type, Instance) @@ -3962,19 +3994,25 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: # As a special case, the alt_promote check makes sure that we don't use the # __radd__ method of int if the LHS is a native int type. - variants_raw = [(right_op, right_type, left_expr), (left_op, left_type, right_expr)] + variants_raw = [ + (rev_op_name, right_op, right_type, left_expr), + (op_name, left_op, left_type, right_expr), + ] else: # In all other cases, we do the usual thing and call __add__ first and # __radd__ second when doing "A() + B()". - variants_raw = [(left_op, left_type, right_expr), (right_op, right_type, left_expr)] + variants_raw = [ + (op_name, left_op, left_type, right_expr), + (rev_op_name, right_op, right_type, left_expr), + ] # STEP 3: # We now filter out all non-existent operators. The 'variants' list contains # all operator methods that are actually present, in the order that Python # attempts to invoke them. - variants = [(op, obj, arg) for (op, obj, arg) in variants_raw if op is not None] + variants = [(na, op, obj, arg) for (na, op, obj, arg) in variants_raw if op is not None] # STEP 4: # We now try invoking each one. If an operation succeeds, end early and return @@ -3983,13 +4021,23 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None: errors = [] results = [] - for method, obj, arg in variants: + for name, method, obj, arg in variants: with self.msg.filter_errors(save_filtered_errors=True) as local_errors: result = self.check_method_call(op_name, obj, method, [arg], [ARG_POS], context) if local_errors.has_new_errors(): errors.append(local_errors.filtered_errors()) results.append(result) else: + if isinstance(obj, Instance) and isinstance( + defn := obj.type.get_method(name), OverloadedFuncDef + ): + for item in defn.items: + if ( + isinstance(item, Decorator) + and isinstance(typ := item.func.type, CallableType) + and bind_self(typ) == result[1] + ): + self.chk.check_deprecated(item.func, context) return result # We finish invoking above operators and no early return happens. Therefore, @@ -4256,6 +4304,7 @@ def visit_unary_expr(self, e: UnaryExpr) -> Type: op = e.op if op == "not": result: Type = self.bool_type() + self.chk.check_for_truthy_type(operand_type, e.expr) else: method = operators.unary_op_methods[op] result, method_type = self.check_method_call_by_name(method, operand_type, [], [], e) @@ -4337,18 +4386,15 @@ def visit_index_with_type( else: return self.nonliteral_tuple_index_helper(left_type, index) elif isinstance(left_type, TypedDictType): - return self.visit_typeddict_index_expr(left_type, e.index) + return self.visit_typeddict_index_expr(left_type, e.index)[0] elif isinstance(left_type, FunctionLike) and left_type.is_type_obj(): if left_type.type_object().is_enum: return self.visit_enum_index_expr(left_type.type_object(), e.index, e) - elif left_type.type_object().type_vars: - return self.named_type("types.GenericAlias") - elif ( - left_type.type_object().fullname == "builtins.type" - and self.chk.options.python_version >= (3, 9) + elif self.chk.options.python_version >= (3, 9) and ( + left_type.type_object().type_vars + or left_type.type_object().fullname == "builtins.type" ): - # builtins.type is special: it's not generic in stubs, but it supports indexing - return self.named_type("typing._SpecialForm") + return self.named_type("types.GenericAlias") if isinstance(left_type, TypeVarType) and not self.has_member( left_type.upper_bound, "__getitem__" @@ -4521,7 +4567,7 @@ def union_tuple_fallback_item(self, left_type: TupleType) -> Type: def visit_typeddict_index_expr( self, td_type: TypedDictType, index: Expression, setitem: bool = False - ) -> Type: + ) -> tuple[Type, set[str]]: if isinstance(index, StrExpr): key_names = [index.value] else: @@ -4544,17 +4590,17 @@ def visit_typeddict_index_expr( key_names.append(key_type.value) else: self.msg.typeddict_key_must_be_string_literal(td_type, index) - return AnyType(TypeOfAny.from_error) + return AnyType(TypeOfAny.from_error), set() value_types = [] for key_name in key_names: value_type = td_type.items.get(key_name) if value_type is None: self.msg.typeddict_key_not_found(td_type, key_name, index, setitem) - return AnyType(TypeOfAny.from_error) + return AnyType(TypeOfAny.from_error), set() else: value_types.append(value_type) - return make_simplified_union(value_types) + return make_simplified_union(value_types), set(key_names) def visit_enum_index_expr( self, enum_type: TypeInfo, index: Expression, context: Context @@ -4682,7 +4728,7 @@ def visit_type_application(self, tapp: TypeApplication) -> Type: """ if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias): if tapp.expr.node.python_3_12_type_alias: - return self.named_type("typing.TypeAliasType") + return self.type_alias_type_type() # Subscription of a (generic) alias in runtime context, expand the alias. item = instantiate_type_alias( tapp.expr.node, @@ -4746,7 +4792,7 @@ class LongName(Generic[T]): ... y = cast(A, ...) """ if alias.python_3_12_type_alias: - return self.named_type("typing.TypeAliasType") + return self.type_alias_type_type() if isinstance(alias.target, Instance) and alias.target.invalid: # type: ignore[misc] # An invalid alias, error already has been reported return AnyType(TypeOfAny.from_error) @@ -5567,11 +5613,15 @@ def visit_slice_expr(self, e: SliceExpr) -> Type: except KeyError: supports_index = self.chk.named_type("builtins.int") # thanks, fixture life expected = make_optional_type(supports_index) + type_args = [] for index in [e.begin_index, e.end_index, e.stride]: if index: t = self.accept(index) self.chk.check_subtype(t, expected, index, message_registry.INVALID_SLICE_INDEX) - return self.named_type("builtins.slice") + type_args.append(t) + else: + type_args.append(NoneType()) + return self.chk.named_generic_type("builtins.slice", type_args) def visit_list_comprehension(self, e: ListComprehension) -> Type: return self.check_generator_or_comprehension( @@ -5585,8 +5635,13 @@ def visit_set_comprehension(self, e: SetComprehension) -> Type: def visit_generator_expr(self, e: GeneratorExpr) -> Type: # If any of the comprehensions use async for, the expression will return an async generator - # object, or if the left-side expression uses await. - if any(e.is_async) or has_await_expression(e.left_expr): + # object, or await is used anywhere but in the leftmost sequence. + if ( + any(e.is_async) + or has_await_expression(e.left_expr) + or any(has_await_expression(sequence) for sequence in e.sequences[1:]) + or any(has_await_expression(cond) for condlist in e.condlists for cond in condlist) + ): typ = "typing.AsyncGenerator" # received type is always None in async generator expressions additional_args: list[Type] = [NoneType()] @@ -5762,15 +5817,20 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F allow_none_return=allow_none_return, ) - # Only create a union type if the type context is a union, to be mostly - # compatible with older mypy versions where we always did a join. - # - # TODO: Always create a union or at least in more cases? - if isinstance(get_proper_type(self.type_context[-1]), UnionType): - res: Type = make_simplified_union([if_type, full_context_else_type]) - else: - res = join.join_types(if_type, else_type) + # In most cases using if_type as a context for right branch gives better inferred types. + # This is however not the case for literal types, so use the full context instead. + if is_literal_type_like(full_context_else_type) and not is_literal_type_like(else_type): + else_type = full_context_else_type + res: Type = make_simplified_union([if_type, else_type]) + if has_uninhabited_component(res) and not isinstance( + get_proper_type(self.type_context[-1]), UnionType + ): + # In rare cases with empty collections join may give a better result. + alternative = join.join_types(if_type, else_type) + p_alt = get_proper_type(alternative) + if not isinstance(p_alt, Instance) or p_alt.type.fullname != "builtins.object": + res = alternative return res def analyze_cond_branch( @@ -5862,6 +5922,12 @@ def named_type(self, name: str) -> Instance: """ return self.chk.named_type(name) + def type_alias_type_type(self) -> Instance: + """Returns a `typing.TypeAliasType` or `typing_extensions.TypeAliasType`.""" + if self.chk.options.python_version >= (3, 12): + return self.named_type("typing.TypeAliasType") + return self.named_type("typing_extensions.TypeAliasType") + def is_valid_var_arg(self, typ: Type) -> bool: """Is a type valid as a *args argument?""" typ = get_proper_type(typ) @@ -6283,23 +6349,7 @@ def __init__(self) -> None: def visit_callable_type(self, t: CallableType) -> bool: # TODO: we need to check only for type variables of original callable. - return self.query_types(t.arg_types) or t.accept(HasTypeVarQuery()) - - -class HasTypeVarQuery(types.BoolTypeQuery): - """Visitor for querying whether a type has a type variable component.""" - - def __init__(self) -> None: - super().__init__(types.ANY_STRATEGY) - - def visit_type_var(self, t: TypeVarType) -> bool: - return True - - def visit_param_spec(self, t: ParamSpecType) -> bool: - return True - - def visit_type_var_tuple(self, t: TypeVarTupleType) -> bool: - return True + return self.query_types(t.arg_types) or has_type_vars(t) def has_erased_component(t: Type | None) -> bool: @@ -6568,3 +6618,12 @@ def get_partial_instance_type(t: Type | None) -> PartialType | None: if t is None or not isinstance(t, PartialType) or t.type is None: return None return t + + +def is_type_type_context(context: Type | None) -> bool: + context = get_proper_type(context) + if isinstance(context, TypeType): + return True + if isinstance(context, UnionType): + return any(is_type_type_context(item) for item in context.items) + return False diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 0f117f5475ed1..50e54ca304606 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -17,6 +17,7 @@ ARG_POS, ARG_STAR, ARG_STAR2, + EXCLUDED_ENUM_ATTRIBUTES, SYMBOL_FUNCBASE_TYPES, Context, Decorator, @@ -48,7 +49,6 @@ type_object_type_from_function, ) from mypy.types import ( - ENUM_REMOVED_PROPS, AnyType, CallableType, DeletedType, @@ -87,6 +87,7 @@ class MemberContext: def __init__( self, + *, is_lvalue: bool, is_super: bool, is_operator: bool, @@ -126,16 +127,16 @@ def copy_modified( original_type: Type | None = None, ) -> MemberContext: mx = MemberContext( - self.is_lvalue, - self.is_super, - self.is_operator, - self.original_type, - self.context, - self.msg, - self.chk, - self.self_type, - self.module_symbol_table, - self.no_deferral, + is_lvalue=self.is_lvalue, + is_super=self.is_super, + is_operator=self.is_operator, + original_type=self.original_type, + context=self.context, + msg=self.msg, + chk=self.chk, + self_type=self.self_type, + module_symbol_table=self.module_symbol_table, + no_deferral=self.no_deferral, ) if messages is not None: mx.msg = messages @@ -152,11 +153,11 @@ def analyze_member_access( name: str, typ: Type, context: Context, + *, is_lvalue: bool, is_super: bool, is_operator: bool, msg: MessageBuilder, - *, original_type: Type, chk: mypy.checker.TypeChecker, override_info: TypeInfo | None = None, @@ -190,12 +191,12 @@ def analyze_member_access( are not available via the type object directly) """ mx = MemberContext( - is_lvalue, - is_super, - is_operator, - original_type, - context, - msg, + is_lvalue=is_lvalue, + is_super=is_super, + is_operator=is_operator, + original_type=original_type, + context=context, + msg=msg, chk=chk, self_type=self_type, module_symbol_table=module_symbol_table, @@ -316,9 +317,12 @@ def analyze_instance_member_access( if method.is_property: assert isinstance(method, OverloadedFuncDef) - first_item = method.items[0] - assert isinstance(first_item, Decorator) - return analyze_var(name, first_item.var, typ, info, mx) + getter = method.items[0] + assert isinstance(getter, Decorator) + if mx.is_lvalue and (len(items := method.items) > 1): + mx.chk.warn_deprecated(items[1], mx.context) + return analyze_var(name, getter.var, typ, info, mx) + if mx.is_lvalue: mx.msg.cant_assign_to_method(mx.context) if not isinstance(method, OverloadedFuncDef): @@ -493,6 +497,8 @@ def analyze_member_var_access( # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, mx.is_lvalue) + mx.chk.warn_deprecated(v, mx.context) + vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. @@ -572,7 +578,11 @@ def analyze_member_var_access( if hook: result = hook( AttributeContext( - get_proper_type(mx.original_type), result, mx.context, mx.chk + get_proper_type(mx.original_type), + result, + mx.is_lvalue, + mx.context, + mx.chk, ) ) return result @@ -628,7 +638,9 @@ def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Cont msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) -def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: +def analyze_descriptor_access( + descriptor_type: Type, mx: MemberContext, *, assignment: bool = False +) -> Type: """Type check descriptor access. Arguments: @@ -709,6 +721,12 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: callable_name=callable_name, ) + if not assignment: + mx.chk.check_deprecated(dunder_get, mx.context) + mx.chk.warn_deprecated_overload_item( + dunder_get, mx.context, target=inferred_dunder_get_type, selftype=descriptor_type + ) + inferred_dunder_get_type = get_proper_type(inferred_dunder_get_type) if isinstance(inferred_dunder_get_type, AnyType): # check_call failed, and will have reported an error @@ -829,7 +847,9 @@ def analyze_var( result = analyze_descriptor_access(result, mx) if hook: result = hook( - AttributeContext(get_proper_type(mx.original_type), result, mx.context, mx.chk) + AttributeContext( + get_proper_type(mx.original_type), result, mx.is_lvalue, mx.context, mx.chk + ) ) return result @@ -1004,6 +1024,8 @@ def analyze_class_attribute_access( # on the class object itself rather than the instance. return None + mx.chk.warn_deprecated(node.node, mx.context) + is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncBase) if mx.is_lvalue: @@ -1148,7 +1170,9 @@ def apply_class_attr_hook( ) -> Type | None: if hook: result = hook( - AttributeContext(get_proper_type(mx.original_type), result, mx.context, mx.chk) + AttributeContext( + get_proper_type(mx.original_type), result, mx.is_lvalue, mx.context, mx.chk + ) ) return result @@ -1157,7 +1181,7 @@ def analyze_enum_class_attribute_access( itype: Instance, name: str, mx: MemberContext ) -> Type | None: # Skip these since Enum will remove it - if name in ENUM_REMOVED_PROPS: + if name in EXCLUDED_ENUM_ATTRIBUTES: return report_missing_attribute(mx.original_type, itype, name, mx) # Dunders and private names are not Enum members if name.startswith("__") and name.replace("_", "") != "": @@ -1185,9 +1209,12 @@ def analyze_typeddict_access( if isinstance(mx.context, IndexExpr): # Since we can get this during `a['key'] = ...` # it is safe to assume that the context is `IndexExpr`. - item_type = mx.chk.expr_checker.visit_typeddict_index_expr( + item_type, key_names = mx.chk.expr_checker.visit_typeddict_index_expr( typ, mx.context.index, setitem=True ) + assigned_readonly_keys = typ.readonly_keys & key_names + if assigned_readonly_keys: + mx.msg.readonly_keys_mutated(assigned_readonly_keys, context=mx.context) else: # It can also be `a.__setitem__(...)` direct call. # In this case `item_type` can be `Any`, diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index a23be464b8252..43f42039b1996 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -46,6 +46,7 @@ TypedDictType, TypeOfAny, TypeVarTupleType, + TypeVarType, UninhabitedType, UnionType, UnpackType, @@ -158,7 +159,8 @@ def visit_or_pattern(self, o: OrPattern) -> PatternType: for pattern in o.patterns: pattern_type = self.accept(pattern, current_type) pattern_types.append(pattern_type) - current_type = pattern_type.rest_type + if not is_uninhabited(pattern_type.type): + current_type = pattern_type.rest_type # # Collect the final type @@ -307,7 +309,7 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: for inner_type, new_inner_type in zip(inner_types, new_inner_types): (narrowed_inner_type, inner_rest_type) = ( self.chk.conditional_types_with_intersection( - new_inner_type, [get_type_range(inner_type)], o, default=new_inner_type + inner_type, [get_type_range(new_inner_type)], o, default=inner_type ) ) narrowed_inner_types.append(narrowed_inner_type) @@ -320,6 +322,16 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: if all(is_uninhabited(typ) for typ in inner_rest_types): # All subpatterns always match, so we can apply negative narrowing rest_type = TupleType(rest_inner_types, current_type.partial_fallback) + elif sum(not is_uninhabited(typ) for typ in inner_rest_types) == 1: + # Exactly one subpattern may conditionally match, the rest always match. + # We can apply negative narrowing to this one position. + rest_type = TupleType( + [ + curr if is_uninhabited(rest) else rest + for curr, rest in zip(inner_types, inner_rest_types) + ], + current_type.partial_fallback, + ) elif isinstance(current_type, TupleType): # For variadic tuples it is too tricky to match individual items like for fixed # tuples, so we instead try to narrow the entire type. @@ -332,13 +344,11 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: new_inner_type = UninhabitedType() for typ in new_inner_types: new_inner_type = join_types(new_inner_type, typ) - new_type = self.construct_sequence_child(current_type, new_inner_type) - if is_subtype(new_type, current_type): - new_type, _ = self.chk.conditional_types_with_intersection( - current_type, [get_type_range(new_type)], o, default=current_type - ) + if isinstance(current_type, TypeVarType): + new_bound = self.narrow_sequence_child(current_type.upper_bound, new_inner_type, o) + new_type = current_type.copy_modified(upper_bound=new_bound) else: - new_type = current_type + new_type = self.narrow_sequence_child(current_type, new_inner_type, o) return PatternType(new_type, rest_type, captures) def get_sequence_type(self, t: Type, context: Context) -> Type | None: @@ -437,6 +447,16 @@ def expand_starred_pattern_types( return new_types + def narrow_sequence_child(self, outer_type: Type, inner_type: Type, ctx: Context) -> Type: + new_type = self.construct_sequence_child(outer_type, inner_type) + if is_subtype(new_type, outer_type): + new_type, _ = self.chk.conditional_types_with_intersection( + outer_type, [get_type_range(new_type)], ctx, default=outer_type + ) + else: + new_type = outer_type + return new_type + def visit_starred_pattern(self, o: StarredPattern) -> PatternType: captures: dict[Expression, Type] = {} if o.capture is not None: @@ -488,7 +508,7 @@ def get_mapping_item_type( with self.msg.filter_errors() as local_errors: result: Type | None = self.chk.expr_checker.visit_typeddict_index_expr( mapping_type, key - ) + )[0] has_local_errors = local_errors.has_new_errors() # If we can't determine the type statically fall back to treating it as a normal # mapping @@ -584,10 +604,10 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: "__match_args__", typ, o, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=typ, chk=self.chk, ) @@ -650,10 +670,10 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: keyword, narrowed_type, pattern, - False, - False, - False, - self.msg, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, original_type=new_type, chk=self.chk, ) @@ -683,7 +703,9 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: def should_self_match(self, typ: Type) -> bool: typ = get_proper_type(typ) - if isinstance(typ, Instance) and typ.type.is_named_tuple: + if isinstance(typ, Instance) and typ.type.get("__match_args__") is not None: + # Named tuples and other subtypes of builtins that define __match_args__ + # should not self match. return False for other in self.self_match_types: if is_subtype(typ, other): diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index c63210a96c443..dd42fe7755a00 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -393,8 +393,10 @@ def check_specs_in_format_call( # If the explicit conversion is given, then explicit conversion is called _first_. if spec.conversion[1] not in "rsa": self.msg.fail( - 'Invalid conversion type "{}",' - ' must be one of "r", "s" or "a"'.format(spec.conversion[1]), + ( + f'Invalid conversion type "{spec.conversion[1]}", ' + f'must be one of "r", "s" or "a"' + ), call, code=codes.STRING_FORMATTING, ) @@ -472,8 +474,7 @@ def find_replacements_in_call(self, call: CallExpr, keys: list[str]) -> list[Exp expr = self.get_expr_by_position(int(key), call) if not expr: self.msg.fail( - "Cannot find replacement for positional" - " format specifier {}".format(key), + f"Cannot find replacement for positional format specifier {key}", call, code=codes.STRING_FORMATTING, ) @@ -654,8 +655,9 @@ class User(TypedDict): assert spec.key, "Call this method only after auto-generating keys!" assert spec.field self.msg.fail( - "Invalid index expression in format field" - ' accessor "{}"'.format(spec.field[len(spec.key) :]), + 'Invalid index expression in format field accessor "{}"'.format( + spec.field[len(spec.key) :] + ), ctx, code=codes.STRING_FORMATTING, ) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a6bf021000c1a..9fa99333a42a9 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -472,12 +472,7 @@ def parse_section( ) continue else: - dv = None - # We have to keep new_semantic_analyzer in Options - # for plugin compatibility but it is not a valid option anymore. - assert hasattr(template, "new_semantic_analyzer") - if key != "new_semantic_analyzer": - dv = getattr(template, key, None) + dv = getattr(template, key, None) if dv is None: if key.endswith("_report"): report_type = key[:-7].replace("_", "-") diff --git a/mypy/constraints.py b/mypy/constraints.py index 49a2aea8fa051..5c815bf2af656 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1029,18 +1029,10 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: if template.type_guard is not None and cactual.type_guard is not None: template_ret_type = template.type_guard cactual_ret_type = cactual.type_guard - elif template.type_guard is not None: - template_ret_type = AnyType(TypeOfAny.special_form) - elif cactual.type_guard is not None: - cactual_ret_type = AnyType(TypeOfAny.special_form) if template.type_is is not None and cactual.type_is is not None: template_ret_type = template.type_is cactual_ret_type = cactual.type_is - elif template.type_is is not None: - template_ret_type = AnyType(TypeOfAny.special_form) - elif cactual.type_is is not None: - cactual_ret_type = AnyType(TypeOfAny.special_form) res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) diff --git a/mypy/copytype.py b/mypy/copytype.py index 465f06566f548..ecb1a89759b67 100644 --- a/mypy/copytype.py +++ b/mypy/copytype.py @@ -107,7 +107,9 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: return self.copy_common(t, TupleType(t.items, t.partial_fallback, implicit=t.implicit)) def visit_typeddict_type(self, t: TypedDictType) -> ProperType: - return self.copy_common(t, TypedDictType(t.items, t.required_keys, t.fallback)) + return self.copy_common( + t, TypedDictType(t.items, t.required_keys, t.readonly_keys, t.fallback) + ) def visit_literal_type(self, t: LiteralType) -> ProperType: return self.copy_common(t, LiteralType(value=t.value, fallback=t.fallback)) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index f8a0f91f87d96..70cfaa5b2fb98 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -625,6 +625,8 @@ def fine_grained_increment_follow_imports( changed, new_files = self.find_reachable_changed_modules( sources, graph, seen, changed_paths ) + # Same as in fine_grained_increment(). + self.add_explicitly_new(sources, changed) if explicit_export_types: # Same as in fine_grained_increment(). add_all_sources_to_changed(sources, changed) @@ -888,6 +890,22 @@ def _find_changed( assert path removed.append((source.module, path)) + self.add_explicitly_new(sources, changed) + + # Find anything that has had its module path change because of added or removed __init__s + last = {s.path: s.module for s in self.previous_sources} + for s in sources: + assert s.path + if s.path in last and last[s.path] != s.module: + # Mark it as removed from its old name and changed at its new name + removed.append((last[s.path], s.path)) + changed.append((s.module, s.path)) + + return changed, removed + + def add_explicitly_new( + self, sources: list[BuildSource], changed: list[tuple[str, str]] + ) -> None: # Always add modules that were (re-)added, since they may be detected as not changed by # fswatcher (if they were actually not changed), but they may still need to be checked # in case they had errors before they were deleted from sources on previous runs. @@ -903,17 +921,6 @@ def _find_changed( ] ) - # Find anything that has had its module path change because of added or removed __init__s - last = {s.path: s.module for s in self.previous_sources} - for s in sources: - assert s.path - if s.path in last and last[s.path] != s.module: - # Mark it as removed from its old name and changed at its new name - removed.append((last[s.path], s.path)) - changed.append((s.module, s.path)) - - return changed, removed - def cmd_inspect( self, show: str, diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index 0baff863b3c34..9b21d78ce599b 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -104,8 +104,7 @@ def truncate(self, size: int | None = 0) -> int: raise io.UnsupportedOperation def write(self, output: str) -> int: - resp: dict[str, Any] = {} - resp[self.output_key] = output + resp: dict[str, Any] = {self.output_key: output} send(self.server, resp) return len(output) diff --git a/mypy/erasetype.py b/mypy/erasetype.py index b41eefcd4821b..222e7f2a6d7ab 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -34,6 +34,7 @@ get_proper_type, get_proper_types, ) +from mypy.typevartuples import erased_vars def erase_type(typ: Type) -> ProperType: @@ -77,17 +78,7 @@ def visit_deleted_type(self, t: DeletedType) -> ProperType: return t def visit_instance(self, t: Instance) -> ProperType: - args: list[Type] = [] - for tv in t.type.defn.type_vars: - # Valid erasure for *Ts is *tuple[Any, ...], not just Any. - if isinstance(tv, TypeVarTupleType): - args.append( - UnpackType( - tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)]) - ) - ) - else: - args.append(AnyType(TypeOfAny.special_form)) + args = erased_vars(t.type.defn.type_vars, TypeOfAny.special_form) return Instance(t.type, args, t.line) def visit_type_var(self, t: TypeVarType) -> ProperType: @@ -170,6 +161,7 @@ class TypeVarEraser(TypeTranslator): """Implementation of type erasure""" def __init__(self, erase_id: Callable[[TypeVarId], bool], replacement: Type) -> None: + super().__init__() self.erase_id = erase_id self.replacement = replacement diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 6e8763264ddd3..5736be5c143ea 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -185,6 +185,9 @@ def __hash__(self) -> int: ANNOTATION_UNCHECKED = ErrorCode( "annotation-unchecked", "Notify about type annotations in unchecked functions", "General" ) +TYPEDDICT_READONLY_MUTATED = ErrorCode( + "typeddict-readonly-mutated", "TypedDict's ReadOnly key is mutated", "General" +) POSSIBLY_UNDEFINED: Final[ErrorCode] = ErrorCode( "possibly-undefined", "Warn about variables that are defined only in some execution paths", @@ -273,6 +276,14 @@ def __hash__(self) -> int: # This is a catch-all for remaining uncategorized errors. MISC: Final[ErrorCode] = ErrorCode("misc", "Miscellaneous other checks", "General") +OVERLOAD_CANNOT_MATCH: Final[ErrorCode] = ErrorCode( + "overload-cannot-match", + "Warn if an @overload signature can never be matched", + "General", + sub_code_of=MISC, +) + + OVERLOAD_OVERLAP: Final[ErrorCode] = ErrorCode( "overload-overlap", "Warn if multiple @overload variants overlap in unsafe ways", @@ -293,5 +304,12 @@ def __hash__(self) -> int: "General", ) +DEPRECATED: Final = ErrorCode( + "deprecated", + "Warn when importing or using deprecated (overloaded) functions, methods or classes", + "General", + default_enabled=False, +) + # This copy will not include any error codes defined later in the plugins. mypy_error_codes = error_codes.copy() diff --git a/mypy/errors.py b/mypy/errors.py index d6dcd4e49e130..1b3f485d19c06 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -20,7 +20,7 @@ # Show error codes for some note-level messages (these usually appear alone # and not as a comment for a previous error-level message). -SHOW_NOTE_CODES: Final = {codes.ANNOTATION_UNCHECKED} +SHOW_NOTE_CODES: Final = {codes.ANNOTATION_UNCHECKED, codes.DEPRECATED} # Do not add notes with links to error code docs to errors with these codes. # We can tweak this set as we get more experience about what is helpful and what is not. @@ -194,6 +194,9 @@ def on_error(self, file: str, info: ErrorInfo) -> bool: Return True to filter out the error, preventing it from being seen by other ErrorWatcher further down the stack and from being recorded by Errors """ + if info.code == codes.DEPRECATED: + return False + self._has_new_errors = True if isinstance(self._filter, bool): should_filter = self._filter @@ -919,9 +922,25 @@ def file_messages(self, path: str, formatter: ErrorFormatter | None = None) -> l self.flushed_files.add(path) source_lines = None if self.options.pretty and self.read_source: - source_lines = self.read_source(path) + # Find shadow file mapping and read source lines if a shadow file exists for the given path. + # If shadow file mapping is not found, read source lines + mapped_path = self.find_shadow_file_mapping(path) + if mapped_path: + source_lines = self.read_source(mapped_path) + else: + source_lines = self.read_source(path) return self.format_messages(error_tuples, source_lines) + def find_shadow_file_mapping(self, path: str) -> str | None: + """Return the shadow file path for a given source file path or None.""" + if self.options.shadow_file is None: + return None + + for i in self.options.shadow_file: + if i[0] == path: + return i[1] + return None + def new_messages(self) -> list[str]: """Return a string list of new error messages. diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 5c4d6af9458e3..b2040ec074c32 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -2,7 +2,7 @@ from typing import Final, Iterable, Mapping, Sequence, TypeVar, cast, overload -from mypy.nodes import ARG_STAR, Var +from mypy.nodes import ARG_STAR, FakeInfo, Var from mypy.state import state from mypy.types import ( ANY_STRATEGY, @@ -179,6 +179,7 @@ class ExpandTypeVisitor(TrivialSyntheticTypeTranslator): variables: Mapping[TypeVarId, Type] # TypeVar id -> TypeVar value def __init__(self, variables: Mapping[TypeVarId, Type]) -> None: + super().__init__() self.variables = variables self.recursive_tvar_guard: dict[TypeVarId, Type | None] = {} @@ -208,6 +209,16 @@ def visit_erased_type(self, t: ErasedType) -> Type: def visit_instance(self, t: Instance) -> Type: args = self.expand_types_with_unpack(list(t.args)) + + if isinstance(t.type, FakeInfo): + # The type checker expands function definitions and bodies + # if they depend on constrained type variables but the body + # might contain a tuple type comment (e.g., # type: (int, float)), + # in which case 't.type' is not yet available. + # + # See: https://github.com/python/mypy/issues/16649 + return t.copy_modified(args=args) + if t.type.fullname == "builtins.tuple": # Normalize Tuple[*Tuple[X, ...], ...] -> Tuple[X, ...] arg = args[0] @@ -444,15 +455,25 @@ def visit_tuple_type(self, t: TupleType) -> Type: return t.copy_modified(items=items, fallback=fallback) def visit_typeddict_type(self, t: TypedDictType) -> Type: + if cached := self.get_cached(t): + return cached fallback = t.fallback.accept(self) assert isinstance(fallback, ProperType) and isinstance(fallback, Instance) - return t.copy_modified(item_types=self.expand_types(t.items.values()), fallback=fallback) + result = t.copy_modified(item_types=self.expand_types(t.items.values()), fallback=fallback) + self.set_cached(t, result) + return result def visit_literal_type(self, t: LiteralType) -> Type: # TODO: Verify this implementation is correct return t def visit_union_type(self, t: UnionType) -> Type: + # Use cache to avoid O(n**2) or worse expansion of types during translation + # (only for large unions, since caching adds overhead) + use_cache = len(t.items) > 3 + if use_cache and (cached := self.get_cached(t)): + return cached + expanded = self.expand_types(t.items) # After substituting for type variables in t.items, some resulting types # might be subtypes of others, however calling make_simplified_union() @@ -465,7 +486,11 @@ def visit_union_type(self, t: UnionType) -> Type: # otherwise a single item union of a type alias will break it. Note this should not # cause infinite recursion since pathological aliases like A = Union[A, B] are # banned at the semantic analysis level. - return get_proper_type(simplified) + result = get_proper_type(simplified) + + if use_cache: + self.set_cached(t, result) + return result def visit_partial_type(self, t: PartialType) -> Type: return t diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index d9bdf2e2b20b8..506194a4b285b 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -2,11 +2,16 @@ from __future__ import annotations +from typing import Callable + from mypy.fastparse import parse_type_string from mypy.nodes import ( + MISSING_FALLBACK, BytesExpr, CallExpr, ComplexExpr, + Context, + DictExpr, EllipsisExpr, Expression, FloatExpr, @@ -19,6 +24,7 @@ RefExpr, StarExpr, StrExpr, + SymbolTableNode, TupleExpr, UnaryExpr, get_member_expr_fullname, @@ -29,9 +35,11 @@ AnyType, CallableArgument, EllipsisType, + Instance, ProperType, RawExpressionType, Type, + TypedDictType, TypeList, TypeOfAny, UnboundType, @@ -55,18 +63,24 @@ def _extract_argument_name(expr: Expression) -> str | None: def expr_to_unanalyzed_type( expr: Expression, - options: Options | None = None, + options: Options, allow_new_syntax: bool = False, _parent: Expression | None = None, allow_unpack: bool = False, + lookup_qualified: Callable[[str, Context], SymbolTableNode | None] | None = None, ) -> ProperType: """Translate an expression to the corresponding type. The result is not semantically analyzed. It can be UnboundType or TypeList. Raise TypeTranslationError if the expression cannot represent a type. + If lookup_qualified is not provided, the expression is expected to be semantically + analyzed. + If allow_new_syntax is True, allow all type syntax independent of the target Python version (used in stubs). + + # TODO: a lot of code here is duplicated in fastparse.py, refactor this. """ # The `parent` parameter is used in recursive calls to provide context for # understanding whether an CallableArgument is ok. @@ -95,19 +109,26 @@ def expr_to_unanalyzed_type( else: args = [expr.index] - if isinstance(expr.base, RefExpr) and expr.base.fullname in ANNOTATED_TYPE_NAMES: - # TODO: this is not the optimal solution as we are basically getting rid - # of the Annotation definition and only returning the type information, - # losing all the annotations. + if isinstance(expr.base, RefExpr): + # Check if the type is Annotated[...]. For this we need the fullname, + # which must be looked up if the expression hasn't been semantically analyzed. + base_fullname = None + if lookup_qualified is not None: + sym = lookup_qualified(base.name, expr) + if sym and sym.node: + base_fullname = sym.node.fullname + else: + base_fullname = expr.base.fullname - return expr_to_unanalyzed_type(args[0], options, allow_new_syntax, expr) - else: - base.args = tuple( - expr_to_unanalyzed_type( - arg, options, allow_new_syntax, expr, allow_unpack=True - ) - for arg in args - ) + if base_fullname is not None and base_fullname in ANNOTATED_TYPE_NAMES: + # TODO: this is not the optimal solution as we are basically getting rid + # of the Annotation definition and only returning the type information, + # losing all the annotations. + return expr_to_unanalyzed_type(args[0], options, allow_new_syntax, expr) + base.args = tuple( + expr_to_unanalyzed_type(arg, options, allow_new_syntax, expr, allow_unpack=True) + for arg in args + ) if not base.args: base.empty_tuple_index = True return base @@ -116,7 +137,7 @@ def expr_to_unanalyzed_type( elif ( isinstance(expr, OpExpr) and expr.op == "|" - and ((options and options.python_version >= (3, 10)) or allow_new_syntax) + and ((options.python_version >= (3, 10)) or allow_new_syntax) ): return UnionType( [ @@ -206,5 +227,26 @@ def expr_to_unanalyzed_type( return UnpackType( expr_to_unanalyzed_type(expr.expr, options, allow_new_syntax), from_star_syntax=True ) + elif isinstance(expr, DictExpr): + if not expr.items: + raise TypeTranslationError() + items: dict[str, Type] = {} + extra_items_from = [] + for item_name, value in expr.items: + if not isinstance(item_name, StrExpr): + if item_name is None: + extra_items_from.append( + expr_to_unanalyzed_type(value, options, allow_new_syntax, expr) + ) + continue + raise TypeTranslationError() + items[item_name.value] = expr_to_unanalyzed_type( + value, options, allow_new_syntax, expr + ) + result = TypedDictType( + items, set(), set(), Instance(MISSING_FALLBACK, ()), expr.line, expr.column + ) + result.extra_items_from = extra_items_from + return result else: raise TypeTranslationError() diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 342cf36d69e8b..a47ed9b536daf 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -17,6 +17,7 @@ ARG_POS, ARG_STAR, ARG_STAR2, + MISSING_FALLBACK, PARAM_SPEC_KIND, TYPE_VAR_KIND, TYPE_VAR_TUPLE_KIND, @@ -42,7 +43,6 @@ EllipsisExpr, Expression, ExpressionStmt, - FakeInfo, FloatExpr, ForStmt, FuncDef, @@ -92,7 +92,7 @@ YieldFromExpr, check_arg_names, ) -from mypy.options import NEW_GENERIC_SYNTAX, Options +from mypy.options import Options from mypy.patterns import ( AsPattern, ClassPattern, @@ -116,6 +116,7 @@ RawExpressionType, TupleType, Type, + TypedDictType, TypeList, TypeOfAny, UnboundType, @@ -180,17 +181,18 @@ def ast3_parse( if sys.version_info >= (3, 12): ast_TypeAlias = ast3.TypeAlias ast_ParamSpec = ast3.ParamSpec + ast_TypeVar = ast3.TypeVar ast_TypeVarTuple = ast3.TypeVarTuple else: ast_TypeAlias = Any ast_ParamSpec = Any + ast_TypeVar = Any ast_TypeVarTuple = Any N = TypeVar("N", bound=Node) # There is no way to create reasonable fallbacks at this stage, # they must be patched later. -MISSING_FALLBACK: Final = FakeInfo("fallback can't be filled out until semanal") _dummy_fallback: Final = Instance(MISSING_FALLBACK, [], -1) TYPE_IGNORE_PATTERN: Final = re.compile(r"[^#]*#\s*type:\s*ignore\s*(.*)") @@ -329,7 +331,12 @@ def parse_type_string( """ try: _, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None) - return RawExpressionType(expr_string, expr_fallback_name, line, column, node=node) + if isinstance(node, (UnboundType, UnionType)) and node.original_str_expr is None: + node.original_str_expr = expr_string + node.original_str_fallback = expr_fallback_name + return node + else: + return RawExpressionType(expr_string, expr_fallback_name, line, column) except (SyntaxError, ValueError): # Note: the parser will raise a `ValueError` instead of a SyntaxError if # the string happens to contain things like \x00. @@ -345,6 +352,15 @@ def is_no_type_check_decorator(expr: ast3.expr) -> bool: return False +def find_disallowed_expression_in_annotation_scope(expr: ast3.expr | None) -> ast3.expr | None: + if expr is None: + return None + for node in ast3.walk(expr): + if isinstance(node, (ast3.Yield, ast3.YieldFrom, ast3.NamedExpr, ast3.Await)): + return node + return None + + class ASTConverter: def __init__( self, @@ -949,17 +965,7 @@ def do_func_def( return_type = AnyType(TypeOfAny.from_error) else: if sys.version_info >= (3, 12) and n.type_params: - if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: - explicit_type_params = self.translate_type_params(n.type_params) - else: - self.fail( - ErrorMessage( - "PEP 695 generics are not yet supported", code=codes.VALID_TYPE - ), - n.type_params[0].lineno, - n.type_params[0].col_offset, - blocker=False, - ) + explicit_type_params = self.translate_type_params(n.type_params) arg_types = [a.type_annotation for a in args] return_type = TypeConverter( @@ -1044,8 +1050,6 @@ def set_type_optional(self, type: Type | None, initializer: Expression | None) - return # Indicate that type should be wrapped in an Optional if arg is initialized to None. optional = isinstance(initializer, NameExpr) and initializer.name == "None" - if isinstance(type, RawExpressionType) and type.node is not None: - type = type.node if isinstance(type, UnboundType): type.optional = optional @@ -1141,15 +1145,7 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: explicit_type_params: list[TypeParam] | None = None if sys.version_info >= (3, 12) and n.type_params: - if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: - explicit_type_params = self.translate_type_params(n.type_params) - else: - self.fail( - ErrorMessage("PEP 695 generics are not yet supported", code=codes.VALID_TYPE), - n.type_params[0].lineno, - n.type_params[0].col_offset, - blocker=False, - ) + explicit_type_params = self.translate_type_params(n.type_params) cdef = ClassDef( n.name, @@ -1174,15 +1170,43 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: self.class_and_function_stack.pop() return cdef + def validate_type_param(self, type_param: ast_TypeVar) -> None: + incorrect_expr = find_disallowed_expression_in_annotation_scope(type_param.bound) + if incorrect_expr is None: + return + if isinstance(incorrect_expr, (ast3.Yield, ast3.YieldFrom)): + self.fail( + message_registry.TYPE_VAR_YIELD_EXPRESSION_IN_BOUND, + type_param.lineno, + type_param.col_offset, + ) + if isinstance(incorrect_expr, ast3.NamedExpr): + self.fail( + message_registry.TYPE_VAR_NAMED_EXPRESSION_IN_BOUND, + type_param.lineno, + type_param.col_offset, + ) + if isinstance(incorrect_expr, ast3.Await): + self.fail( + message_registry.TYPE_VAR_AWAIT_EXPRESSION_IN_BOUND, + type_param.lineno, + type_param.col_offset, + ) + def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: explicit_type_params = [] for p in type_params: - bound = None + bound: Type | None = None values: list[Type] = [] + default: Type | None = None + if sys.version_info >= (3, 13): + default = TypeConverter(self.errors, line=p.lineno).visit(p.default_value) if isinstance(p, ast_ParamSpec): # type: ignore[misc] - explicit_type_params.append(TypeParam(p.name, PARAM_SPEC_KIND, None, [])) + explicit_type_params.append(TypeParam(p.name, PARAM_SPEC_KIND, None, [], default)) elif isinstance(p, ast_TypeVarTuple): # type: ignore[misc] - explicit_type_params.append(TypeParam(p.name, TYPE_VAR_TUPLE_KIND, None, [])) + explicit_type_params.append( + TypeParam(p.name, TYPE_VAR_TUPLE_KIND, None, [], default) + ) else: if isinstance(p.bound, ast3.Tuple): if len(p.bound.elts) < 2: @@ -1196,8 +1220,11 @@ def translate_type_params(self, type_params: list[Any]) -> list[TypeParam]: conv = TypeConverter(self.errors, line=p.lineno) values = [conv.visit(t) for t in p.bound.elts] elif p.bound is not None: + self.validate_type_param(p) bound = TypeConverter(self.errors, line=p.lineno).visit(p.bound) - explicit_type_params.append(TypeParam(p.name, TYPE_VAR_KIND, bound, values)) + explicit_type_params.append( + TypeParam(p.name, TYPE_VAR_KIND, bound, values, default) + ) return explicit_type_params # Return(expr? value) @@ -1785,29 +1812,31 @@ def visit_MatchOr(self, n: MatchOr) -> OrPattern: node = OrPattern([self.visit(pattern) for pattern in n.patterns]) return self.set_line(node, n) + def validate_type_alias(self, n: ast_TypeAlias) -> None: + incorrect_expr = find_disallowed_expression_in_annotation_scope(n.value) + if incorrect_expr is None: + return + if isinstance(incorrect_expr, (ast3.Yield, ast3.YieldFrom)): + self.fail(message_registry.TYPE_ALIAS_WITH_YIELD_EXPRESSION, n.lineno, n.col_offset) + if isinstance(incorrect_expr, ast3.NamedExpr): + self.fail(message_registry.TYPE_ALIAS_WITH_NAMED_EXPRESSION, n.lineno, n.col_offset) + if isinstance(incorrect_expr, ast3.Await): + self.fail(message_registry.TYPE_ALIAS_WITH_AWAIT_EXPRESSION, n.lineno, n.col_offset) + # TypeAlias(identifier name, type_param* type_params, expr value) def visit_TypeAlias(self, n: ast_TypeAlias) -> TypeAliasStmt | AssignmentStmt: node: TypeAliasStmt | AssignmentStmt - if NEW_GENERIC_SYNTAX in self.options.enable_incomplete_feature: - type_params = self.translate_type_params(n.type_params) - value = self.visit(n.value) - # Since the value is evaluated lazily, wrap the value inside a lambda. - # This helps mypyc. - ret = ReturnStmt(value) - self.set_line(ret, n.value) - value_func = LambdaExpr(body=Block([ret])) - self.set_line(value_func, n.value) - node = TypeAliasStmt(self.visit_Name(n.name), type_params, value_func) - return self.set_line(node, n) - else: - self.fail( - ErrorMessage("PEP 695 type aliases are not yet supported", code=codes.VALID_TYPE), - n.lineno, - n.col_offset, - blocker=False, - ) - node = AssignmentStmt([NameExpr(n.name.id)], self.visit(n.value)) - return self.set_line(node, n) + type_params = self.translate_type_params(n.type_params) + self.validate_type_alias(n) + value = self.visit(n.value) + # Since the value is evaluated lazily, wrap the value inside a lambda. + # This helps mypyc. + ret = ReturnStmt(value) + self.set_line(ret, n.value) + value_func = LambdaExpr(body=Block([ret])) + self.set_line(value_func, n.value) + node = TypeAliasStmt(self.visit_Name(n.name), type_params, value_func) + return self.set_line(node, n) class TypeConverter: @@ -2004,7 +2033,7 @@ def visit_UnaryOp(self, n: UnaryOp) -> Type: if ( isinstance(typ, RawExpressionType) # Use type() because we do not want to allow bools. - and type(typ.literal_value) is int # noqa: E721 + and type(typ.literal_value) is int ): if isinstance(n.op, USub): typ.literal_value *= -1 @@ -2096,6 +2125,22 @@ def visit_Tuple(self, n: ast3.Tuple) -> Type: column=self.convert_column(n.col_offset), ) + def visit_Dict(self, n: ast3.Dict) -> Type: + if not n.keys: + return self.invalid_type(n) + items: dict[str, Type] = {} + extra_items_from = [] + for item_name, value in zip(n.keys, n.values): + if not isinstance(item_name, ast3.Constant) or not isinstance(item_name.value, str): + if item_name is None: + extra_items_from.append(self.visit(value)) + continue + return self.invalid_type(n) + items[item_name.value] = self.visit(value) + result = TypedDictType(items, set(), set(), _dummy_fallback, n.lineno, n.col_offset) + result.extra_items_from = extra_items_from + return result + # Attribute(expr value, identifier attr, expr_context ctx) def visit_Attribute(self, n: Attribute) -> Type: before_dot = self.visit(n.value) diff --git a/mypy/fscache.py b/mypy/fscache.py index 15679ad03e859..8251f4bd94880 100644 --- a/mypy/fscache.py +++ b/mypy/fscache.py @@ -51,8 +51,8 @@ def set_package_root(self, package_root: list[str]) -> None: def flush(self) -> None: """Start another transaction and empty all caches.""" - self.stat_cache: dict[str, os.stat_result] = {} - self.stat_error_cache: dict[str, OSError] = {} + self.stat_or_none_cache: dict[str, os.stat_result | None] = {} + self.listdir_cache: dict[str, list[str]] = {} self.listdir_error_cache: dict[str, OSError] = {} self.isfile_case_cache: dict[str, bool] = {} @@ -62,24 +62,21 @@ def flush(self) -> None: self.hash_cache: dict[str, str] = {} self.fake_package_cache: set[str] = set() - def stat(self, path: str) -> os.stat_result: - if path in self.stat_cache: - return self.stat_cache[path] - if path in self.stat_error_cache: - raise copy_os_error(self.stat_error_cache[path]) + def stat_or_none(self, path: str) -> os.stat_result | None: + if path in self.stat_or_none_cache: + return self.stat_or_none_cache[path] + + st = None try: st = os.stat(path) - except OSError as err: + except OSError: if self.init_under_package_root(path): try: - return self._fake_init(path) + st = self._fake_init(path) except OSError: pass - # Take a copy to get rid of associated traceback and frame objects. - # Just assigning to __traceback__ doesn't free them. - self.stat_error_cache[path] = copy_os_error(err) - raise err - self.stat_cache[path] = st + + self.stat_or_none_cache[path] = st return st def init_under_package_root(self, path: str) -> bool: @@ -112,9 +109,9 @@ def init_under_package_root(self, path: str) -> bool: if not os.path.basename(dirname).isidentifier(): # Can't put an __init__.py in a place that's not an identifier return False - try: - st = self.stat(dirname) - except OSError: + + st = self.stat_or_none(dirname) + if st is None: return False else: if not stat.S_ISDIR(st.st_mode): @@ -145,7 +142,7 @@ def _fake_init(self, path: str) -> os.stat_result: assert basename == "__init__.py", path assert not os.path.exists(path), path # Not cached! dirname = os.path.normpath(dirname) - st = self.stat(dirname) # May raise OSError + st = os.stat(dirname) # May raise OSError # Get stat result as a list so we can modify it. seq: list[float] = list(st) seq[stat.ST_MODE] = stat.S_IFREG | 0o444 @@ -153,7 +150,6 @@ def _fake_init(self, path: str) -> os.stat_result: seq[stat.ST_NLINK] = 1 seq[stat.ST_SIZE] = 0 st = os.stat_result(seq) - self.stat_cache[path] = st # Make listdir() and read() also pretend this file exists. self.fake_package_cache.add(dirname) return st @@ -181,9 +177,8 @@ def listdir(self, path: str) -> list[str]: return results def isfile(self, path: str) -> bool: - try: - st = self.stat(path) - except OSError: + st = self.stat_or_none(path) + if st is None: return False return stat.S_ISREG(st.st_mode) @@ -248,18 +243,14 @@ def exists_case(self, path: str, prefix: str) -> bool: return res def isdir(self, path: str) -> bool: - try: - st = self.stat(path) - except OSError: + st = self.stat_or_none(path) + if st is None: return False return stat.S_ISDIR(st.st_mode) def exists(self, path: str) -> bool: - try: - self.stat(path) - except FileNotFoundError: - return False - return True + st = self.stat_or_none(path) + return st is not None def read(self, path: str) -> bytes: if path in self.read_cache: @@ -269,7 +260,7 @@ def read(self, path: str) -> bytes: # Need to stat first so that the contents of file are from no # earlier instant than the mtime reported by self.stat(). - self.stat(path) + self.stat_or_none(path) dirname, basename = os.path.split(path) dirname = os.path.normpath(dirname) @@ -294,8 +285,10 @@ def hash_digest(self, path: str) -> str: return self.hash_cache[path] def samefile(self, f1: str, f2: str) -> bool: - s1 = self.stat(f1) - s2 = self.stat(f2) + s1 = self.stat_or_none(f1) + s2 = self.stat_or_none(f2) + if s1 is None or s2 is None: + return False return os.path.samestat(s1, s2) diff --git a/mypy/fswatcher.py b/mypy/fswatcher.py index a574a36a0cc5f..97a62ca9f9f7f 100644 --- a/mypy/fswatcher.py +++ b/mypy/fswatcher.py @@ -2,6 +2,7 @@ from __future__ import annotations +import os from typing import AbstractSet, Iterable, NamedTuple from mypy.fscache import FileSystemCache @@ -56,8 +57,7 @@ def remove_watched_paths(self, paths: Iterable[str]) -> None: del self._file_data[path] self._paths -= set(paths) - def _update(self, path: str) -> None: - st = self.fs.stat(path) + def _update(self, path: str, st: os.stat_result) -> None: hash_digest = self.fs.hash_digest(path) self._file_data[path] = FileData(st.st_mtime, st.st_size, hash_digest) @@ -65,9 +65,8 @@ def _find_changed(self, paths: Iterable[str]) -> AbstractSet[str]: changed = set() for path in paths: old = self._file_data[path] - try: - st = self.fs.stat(path) - except FileNotFoundError: + st = self.fs.stat_or_none(path) + if st is None: if old is not None: # File was deleted. changed.add(path) @@ -76,13 +75,13 @@ def _find_changed(self, paths: Iterable[str]) -> AbstractSet[str]: if old is None: # File is new. changed.add(path) - self._update(path) + self._update(path, st) # Round mtimes down, to match the mtimes we write to meta files elif st.st_size != old.st_size or int(st.st_mtime) != int(old.st_mtime): # Only look for changes if size or mtime has changed as an # optimization, since calculating hash is expensive. new_hash = self.fs.hash_digest(path) - self._update(path) + self._update(path, st) if st.st_size != old.st_size or new_hash != old.hash: # Changed file. changed.add(path) diff --git a/mypy/gclogger.py b/mypy/gclogger.py index 75f754ddf4d52..d111e609223c2 100644 --- a/mypy/gclogger.py +++ b/mypy/gclogger.py @@ -38,10 +38,11 @@ def __exit__(self, *args: object) -> None: def get_stats(self) -> Mapping[str, float]: end_time = time.time() - result = {} - result["gc_time"] = self.gc_time - result["gc_calls"] = self.gc_calls - result["gc_collected"] = self.gc_collected - result["gc_uncollectable"] = self.gc_uncollectable - result["build_time"] = end_time - self.start_time + result = { + "gc_time": self.gc_time, + "gc_calls": self.gc_calls, + "gc_collected": self.gc_collected, + "gc_uncollectable": self.gc_uncollectable, + "build_time": end_time - self.start_time, + } return result diff --git a/mypy/inspections.py b/mypy/inspections.py index 3e660a0bd7a69..0baf0896f7e5c 100644 --- a/mypy/inspections.py +++ b/mypy/inspections.py @@ -469,8 +469,7 @@ def missing_type(self, expression: Expression) -> str: def missing_node(self, expression: Expression) -> str: return ( - f'Cannot find definition for "{type(expression).__name__}"' - f" at {expr_span(expression)}" + f'Cannot find definition for "{type(expression).__name__}" at {expr_span(expression)}' ) def add_prefixes(self, result: str, expression: Expression) -> str: diff --git a/mypy/ipc.py b/mypy/ipc.py index ab01f1b79e7d8..991f9ac56652a 100644 --- a/mypy/ipc.py +++ b/mypy/ipc.py @@ -17,9 +17,8 @@ if sys.platform == "win32": # This may be private, but it is needed for IPC on Windows, and is basically stable - import ctypes - import _winapi + import ctypes _IPCHandle = int diff --git a/mypy/join.py b/mypy/join.py index 5284be7dd2a14..2ada7479789b3 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -317,7 +317,7 @@ def visit_none_type(self, t: NoneType) -> ProperType: if state.strict_optional: if isinstance(self.s, (NoneType, UninhabitedType)): return t - elif isinstance(self.s, UnboundType): + elif isinstance(self.s, (UnboundType, AnyType)): return AnyType(TypeOfAny.special_form) else: return mypy.typeops.make_simplified_union([self.s, t]) @@ -631,10 +631,13 @@ def visit_typeddict_type(self, t: TypedDictType) -> ProperType: ) } fallback = self.s.create_anonymous_fallback() + all_keys = set(items.keys()) # We need to filter by items.keys() since some required keys present in both t and # self.s might be missing from the join if the types are incompatible. - required_keys = set(items.keys()) & t.required_keys & self.s.required_keys - return TypedDictType(items, required_keys, fallback) + required_keys = all_keys & t.required_keys & self.s.required_keys + # If one type has a key as readonly, we mark it as readonly for both: + readonly_keys = (t.readonly_keys | t.readonly_keys) & all_keys + return TypedDictType(items, required_keys, readonly_keys, fallback) elif isinstance(self.s, Instance): return join_types(self.s, t.fallback) else: diff --git a/mypy/main.py b/mypy/main.py index 489ef8fd9a7b5..7032682c9fd07 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -19,12 +19,18 @@ validate_package_allow_list, ) from mypy.error_formatter import OUTPUT_CHOICES -from mypy.errorcodes import error_codes from mypy.errors import CompileError from mypy.find_sources import InvalidSourceList, create_source_list from mypy.fscache import FileSystemCache -from mypy.modulefinder import BuildSource, FindModuleCache, SearchPaths, get_search_dirs, mypy_path -from mypy.options import COMPLETE_FEATURES, INCOMPLETE_FEATURES, BuildType, Options +from mypy.modulefinder import ( + BuildSource, + FindModuleCache, + ModuleNotFoundReason, + SearchPaths, + get_search_dirs, + mypy_path, +) +from mypy.options import INCOMPLETE_FEATURES, BuildType, Options from mypy.split_namespace import SplitNamespace from mypy.version import __version__ @@ -546,15 +552,16 @@ def add_invertible_flag( ) config_group.add_argument( "--config-file", - help="Configuration file, must have a [mypy] section " - "(defaults to {})".format(", ".join(defaults.CONFIG_FILES)), + help=( + f"Configuration file, must have a [mypy] section " + f"(defaults to {', '.join(defaults.CONFIG_FILES)})" + ), ) add_invertible_flag( "--warn-unused-configs", default=False, strict_flag=True, - help="Warn about unused '[mypy-]' or '[[tool.mypy.overrides]]' " - "config sections", + help="Warn about unused '[mypy-]' or '[[tool.mypy.overrides]]' config sections", group=config_group, ) @@ -565,7 +572,7 @@ def add_invertible_flag( "--no-namespace-packages", dest="namespace_packages", default=True, - help="Support namespace packages (PEP 420, __init__.py-less)", + help="Disable support for namespace packages (PEP 420, __init__.py-less)", group=imports_group, ) imports_group.add_argument( @@ -573,6 +580,11 @@ def add_invertible_flag( action="store_true", help="Silently ignore imports of missing modules", ) + imports_group.add_argument( + "--follow-untyped-imports", + action="store_true", + help="Typecheck modules without stubs or py.typed marker", + ) imports_group.add_argument( "--follow-imports", choices=["normal", "silent", "skip", "error"], @@ -583,8 +595,7 @@ def add_invertible_flag( "--python-executable", action="store", metavar="EXECUTABLE", - help="Python executable used for finding PEP 561 compliant installed" - " packages and stubs", + help="Python executable used for finding PEP 561 compliant installed packages and stubs", dest="special-opts:python_executable", ) imports_group.add_argument( @@ -617,8 +628,7 @@ def add_invertible_flag( "--platform", action="store", metavar="PLATFORM", - help="Type check special-cased code for the given OS platform " - "(defaults to sys.platform)", + help="Type check special-cased code for the given OS platform (defaults to sys.platform)", ) platform_group.add_argument( "--always-true", @@ -639,12 +649,6 @@ def add_invertible_flag( title="Disallow dynamic typing", description="Disallow the use of the dynamic 'Any' type under certain conditions.", ) - disallow_any_group.add_argument( - "--disallow-any-unimported", - default=False, - action="store_true", - help="Disallow Any types resulting from unfollowed imports", - ) disallow_any_group.add_argument( "--disallow-any-expr", default=False, @@ -655,8 +659,7 @@ def add_invertible_flag( "--disallow-any-decorated", default=False, action="store_true", - help="Disallow functions that have Any in their signature " - "after decorator transformation", + help="Disallow functions that have Any in their signature after decorator transformation", ) disallow_any_group.add_argument( "--disallow-any-explicit", @@ -671,6 +674,12 @@ def add_invertible_flag( help="Disallow usage of generic types that do not specify explicit type parameters", group=disallow_any_group, ) + add_invertible_flag( + "--disallow-any-unimported", + default=False, + help="Disallow Any types resulting from unfollowed imports", + group=disallow_any_group, + ) add_invertible_flag( "--disallow-subclassing-any", default=False, @@ -800,6 +809,13 @@ def add_invertible_flag( help="Warn about statements or expressions inferred to be unreachable", group=lint_group, ) + add_invertible_flag( + "--report-deprecated-as-note", + default=False, + strict_flag=False, + help="Report importing or using deprecated features as notes instead of errors", + group=lint_group, + ) # Note: this group is intentionally added here even though we don't add # --strict to this group near the end. @@ -1336,28 +1352,8 @@ def set_strict_flags() -> None: validate_package_allow_list(options.untyped_calls_exclude) - # Process `--enable-error-code` and `--disable-error-code` flags - disabled_codes = set(options.disable_error_code) - enabled_codes = set(options.enable_error_code) - - valid_error_codes = set(error_codes.keys()) - - invalid_codes = (enabled_codes | disabled_codes) - valid_error_codes - if invalid_codes: - parser.error(f"Invalid error code(s): {', '.join(sorted(invalid_codes))}") - - options.disabled_error_codes |= {error_codes[code] for code in disabled_codes} - options.enabled_error_codes |= {error_codes[code] for code in enabled_codes} - - # Enabling an error code always overrides disabling - options.disabled_error_codes -= options.enabled_error_codes - - # Validate incomplete features. - for feature in options.enable_incomplete_feature: - if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES: - parser.error(f"Unknown incomplete feature: {feature}") - if feature in COMPLETE_FEATURES: - print(f"Warning: {feature} is already enabled by default") + options.process_error_codes(error_callback=parser.error) + options.process_incomplete_features(error_callback=parser.error, warning_callback=print) # Compute absolute path for custom typeshed (if present). if options.custom_typeshed_dir is not None: @@ -1427,7 +1423,15 @@ def set_strict_flags() -> None: fail(f"Package name '{p}' cannot have a slash in it.", stderr, options) p_targets = cache.find_modules_recursive(p) if not p_targets: - fail(f"Can't find package '{p}'", stderr, options) + reason = cache.find_module(p) + if reason is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: + fail( + f"Package '{p}' cannot be type checked due to missing py.typed marker. See https://mypy.readthedocs.io/en/stable/installed_packages.html for more details", + stderr, + options, + ) + else: + fail(f"Can't find package '{p}'", stderr, options) targets.extend(p_targets) for m in special_opts.modules: targets.append(BuildSource(None, m, None)) @@ -1478,7 +1482,7 @@ def process_package_roots( root = "" package_root.append(root) options.package_root = package_root - # Pass the package root on the the filesystem cache. + # Pass the package root on the filesystem cache. fscache.set_package_root(package_root) diff --git a/mypy/meet.py b/mypy/meet.py index 91abf43c0877b..cbe3e99cdcd82 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -223,7 +223,11 @@ def get_possible_variants(typ: Type) -> list[Type]: else: return [typ.upper_bound] elif isinstance(typ, ParamSpecType): - return [typ.upper_bound] + # Extract 'object' from the final mro item + upper_bound = get_proper_type(typ.upper_bound) + if isinstance(upper_bound, Instance): + return [Instance(upper_bound.type.mro[-1], [])] + return [AnyType(TypeOfAny.implementation_artifact)] elif isinstance(typ, TypeVarTupleType): return [typ.upper_bound] elif isinstance(typ, UnionType): @@ -243,8 +247,8 @@ def is_enum_overlapping_union(x: ProperType, y: ProperType) -> bool: and x.type.is_enum and isinstance(y, UnionType) and any( - isinstance(p, LiteralType) and x.type == p.fallback.type - for p in (get_proper_type(z) for z in y.relevant_items()) + isinstance(p := get_proper_type(z), LiteralType) and x.type == p.fallback.type + for z in y.relevant_items() ) ) @@ -687,7 +691,7 @@ def __init__(self, s: ProperType) -> None: def visit_unbound_type(self, t: UnboundType) -> ProperType: if isinstance(self.s, NoneType): if state.strict_optional: - return AnyType(TypeOfAny.special_form) + return UninhabitedType() else: return self.s elif isinstance(self.s, UninhabitedType): @@ -1017,7 +1021,8 @@ def visit_typeddict_type(self, t: TypedDictType) -> ProperType: items = dict(item_list) fallback = self.s.create_anonymous_fallback() required_keys = t.required_keys | self.s.required_keys - return TypedDictType(items, required_keys, fallback) + readonly_keys = t.readonly_keys | self.s.readonly_keys + return TypedDictType(items, required_keys, readonly_keys, fallback) elif isinstance(self.s, Instance) and is_subtype(t, self.s): return t else: @@ -1139,6 +1144,9 @@ def typed_dict_mapping_overlap( - TypedDict(x=str, y=str, total=False) doesn't overlap with Dict[str, int] - TypedDict(x=int, y=str, total=False) overlaps with Dict[str, str] + * A TypedDict with at least one ReadOnly[] key does not overlap + with Dict or MutableMapping, because they assume mutable data. + As usual empty, dictionaries lie in a gray area. In general, List[str] and List[str] are considered non-overlapping despite empty list belongs to both. However, List[int] and List[Never] are considered overlapping. @@ -1159,6 +1167,12 @@ def typed_dict_mapping_overlap( assert isinstance(right, TypedDictType) typed, other = right, left + mutable_mapping = next( + (base for base in other.type.mro if base.fullname == "typing.MutableMapping"), None + ) + if mutable_mapping is not None and typed.readonly_keys: + return False + mapping = next(base for base in other.type.mro if base.fullname == "typing.Mapping") other = map_instance_to_supertype(other, mapping) key_type, value_type = get_proper_types(other.args) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index befacc9e61822..346a677a8e85b 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -138,6 +138,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPEDDICT_KEY_MUST_BE_STRING_LITERAL: Final = ErrorMessage( "Expected TypedDict key to be string literal" ) +TYPEDDICT_OVERRIDE_MERGE: Final = 'Overwriting TypedDict field "{}" while merging' MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?") DUPLICATE_TYPE_SIGNATURES: Final = ErrorMessage("Function has duplicate type signatures") DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable") @@ -195,8 +196,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPEVAR_ARG_MUST_BE_TYPE: Final = '{} "{}" must be a type' TYPEVAR_UNEXPECTED_ARGUMENT: Final = 'Unexpected argument to "TypeVar()"' UNBOUND_TYPEVAR: Final = ( - "A function returning TypeVar should receive at least " - "one argument containing the same TypeVar" + "A function returning TypeVar should receive at least one argument containing the same TypeVar" ) TYPE_PARAMETERS_SHOULD_BE_DECLARED: Final = ( "All type parameters should be declared ({} not declared)" @@ -337,3 +337,31 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TYPE_VAR_TOO_FEW_CONSTRAINED_TYPES: Final = ErrorMessage( "Type variable must have at least two constrained types", codes.MISC ) + +TYPE_VAR_YIELD_EXPRESSION_IN_BOUND: Final = ErrorMessage( + "Yield expression cannot be used as a type variable bound", codes.SYNTAX +) + +TYPE_VAR_NAMED_EXPRESSION_IN_BOUND: Final = ErrorMessage( + "Named expression cannot be used as a type variable bound", codes.SYNTAX +) + +TYPE_VAR_AWAIT_EXPRESSION_IN_BOUND: Final = ErrorMessage( + "Await expression cannot be used as a type variable bound", codes.SYNTAX +) + +TYPE_VAR_GENERIC_CONSTRAINT_TYPE: Final = ErrorMessage( + "TypeVar constraint type cannot be parametrized by type variables", codes.MISC +) + +TYPE_ALIAS_WITH_YIELD_EXPRESSION: Final = ErrorMessage( + "Yield expression cannot be used within a type alias", codes.SYNTAX +) + +TYPE_ALIAS_WITH_NAMED_EXPRESSION: Final = ErrorMessage( + "Named expression cannot be used within a type alias", codes.SYNTAX +) + +TYPE_ALIAS_WITH_AWAIT_EXPRESSION: Final = ErrorMessage( + "Await expression cannot be used within a type alias", codes.SYNTAX +) diff --git a/mypy/messages.py b/mypy/messages.py index 62846c536f3d2..6b0760cd79c6f 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -675,8 +675,7 @@ def incompatible_argument( arg_type, callee.arg_types[n - 1], options=self.options ) info = ( - f" (expression has type {arg_type_str}, " - f"target has type {callee_type_str})" + f" (expression has type {arg_type_str}, target has type {callee_type_str})" ) error_msg = ( message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT.with_additional_msg(info) @@ -871,7 +870,10 @@ def incompatible_argument_note( ) if call: self.note_call(original_caller_type, call, context, code=code) - + if isinstance(callee_type, Instance) and callee_type.type.is_protocol: + call = find_member("__call__", callee_type, callee_type, is_operator=True) + if call: + self.note_call(callee_type, call, context, code=code) self.maybe_note_concatenate_pos_args(original_caller_type, callee_type, context, code) def maybe_note_concatenate_pos_args( @@ -926,6 +928,17 @@ def invalid_index_type( code=code, ) + def readonly_keys_mutated(self, keys: set[str], context: Context) -> None: + if len(keys) == 1: + suffix = "is" + else: + suffix = "are" + self.fail( + "ReadOnly {} TypedDict {} mutated".format(format_key_list(sorted(keys)), suffix), + code=codes.TYPEDDICT_READONLY_MUTATED, + context=context, + ) + def too_few_arguments( self, callee: CallableType, context: Context, argument_names: Sequence[str | None] | None ) -> None: @@ -1384,7 +1397,7 @@ def could_not_infer_type_arguments( self.fail("Cannot infer function type argument", context) def invalid_var_arg(self, typ: Type, context: Context) -> None: - self.fail("List or tuple expected as variadic arguments", context) + self.fail("Expected iterable as variadic argument", context) def invalid_keyword_var_arg(self, typ: Type, is_mapping: bool, context: Context) -> None: typ = get_proper_type(typ) @@ -1422,8 +1435,7 @@ def first_argument_for_super_must_be_type(self, actual: Type, context: Context) def unsafe_super(self, method: str, cls: str, ctx: Context) -> None: self.fail( - 'Call to abstract method "{}" of "{}" with trivial body' - " via super() is unsafe".format(method, cls), + f'Call to abstract method "{method}" of "{cls}" with trivial body via super() is unsafe', ctx, code=codes.SAFE_SUPER, ) @@ -1577,8 +1589,10 @@ def final_cant_override_writable(self, name: str, ctx: Context) -> None: def cant_override_final(self, name: str, base_name: str, ctx: Context) -> None: self.fail( - 'Cannot override final attribute "{}"' - ' (previously declared in base class "{}")'.format(name, base_name), + ( + f'Cannot override final attribute "{name}" ' + f'(previously declared in base class "{base_name}")' + ), ctx, ) @@ -1653,6 +1667,7 @@ def overloaded_signature_will_never_match( index1=index1, index2=index2 ), context, + code=codes.OVERLOAD_CANNOT_MATCH, ) def overloaded_signatures_typevar_specific(self, index: int, context: Context) -> None: @@ -1664,15 +1679,16 @@ def overloaded_signatures_typevar_specific(self, index: int, context: Context) - def overloaded_signatures_arg_specific(self, index: int, context: Context) -> None: self.fail( - "Overloaded function implementation does not accept all possible arguments " - "of signature {}".format(index), + ( + f"Overloaded function implementation does not accept all possible arguments " + f"of signature {index}" + ), context, ) def overloaded_signatures_ret_specific(self, index: int, context: Context) -> None: self.fail( - "Overloaded function implementation cannot produce return type " - "of signature {}".format(index), + f"Overloaded function implementation cannot produce return type of signature {index}", context, ) @@ -1695,8 +1711,7 @@ def operator_method_signatures_overlap( context: Context, ) -> None: self.fail( - 'Signatures of "{}" of "{}" and "{}" of {} ' - "are unsafely overlapping".format( + 'Signatures of "{}" of "{}" and "{}" of {} are unsafely overlapping'.format( reverse_method, reverse_class.name, forward_method, @@ -1985,8 +2000,7 @@ def bad_proto_variance( self, actual: int, tvar_name: str, expected: int, context: Context ) -> None: msg = capitalize( - '{} type variable "{}" used in protocol where' - " {} one is expected".format( + '{} type variable "{}" used in protocol where {} one is expected'.format( variance_string(actual), tvar_name, variance_string(expected) ) ) @@ -2234,15 +2248,17 @@ def report_protocol_problems( for name, subflags, superflags in conflict_flags[:MAX_ITEMS]: if not class_obj and IS_CLASSVAR in subflags and IS_CLASSVAR not in superflags: self.note( - "Protocol member {}.{} expected instance variable," - " got class variable".format(supertype.type.name, name), + "Protocol member {}.{} expected instance variable, got class variable".format( + supertype.type.name, name + ), context, code=code, ) if not class_obj and IS_CLASSVAR in superflags and IS_CLASSVAR not in subflags: self.note( - "Protocol member {}.{} expected class variable," - " got instance variable".format(supertype.type.name, name), + "Protocol member {}.{} expected class variable, got instance variable".format( + supertype.type.name, name + ), context, code=code, ) @@ -2612,10 +2628,13 @@ def format_literal_value(typ: LiteralType) -> str: return format(typ.fallback) items = [] for item_name, item_type in typ.items.items(): - modifier = "" if item_name in typ.required_keys else "?" + modifier = "" + if item_name not in typ.required_keys: + modifier += "?" + if item_name in typ.readonly_keys: + modifier += "=" items.append(f"{item_name!r}{modifier}: {format(item_type)}") - s = f"TypedDict({{{', '.join(items)}}})" - return s + return f"TypedDict({{{', '.join(items)}}})" elif isinstance(typ, LiteralType): return f"Literal[{format_literal_value(typ)}]" elif isinstance(typ, UnionType): @@ -2941,9 +2960,9 @@ def [T <: int] f(self, x: int, y: T) -> None for tvar in tp.variables: if isinstance(tvar, TypeVarType): upper_bound = get_proper_type(tvar.upper_bound) - if ( + if not ( isinstance(upper_bound, Instance) - and upper_bound.type.fullname != "builtins.object" + and upper_bound.type.fullname == "builtins.object" ): tvars.append(f"{tvar.name}: {format_type_bare(upper_bound, options)}") elif tvar.values: diff --git a/mypy/metastore.py b/mypy/metastore.py index 4caa7d7f0534e..ece397360e5b2 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -27,20 +27,20 @@ class MetadataStore: @abstractmethod def getmtime(self, name: str) -> float: - """Read the mtime of a metadata entry.. + """Read the mtime of a metadata entry. Raises FileNotFound if the entry does not exist. """ @abstractmethod - def read(self, name: str) -> str: + def read(self, name: str) -> bytes: """Read the contents of a metadata entry. Raises FileNotFound if the entry does not exist. """ @abstractmethod - def write(self, name: str, data: str, mtime: float | None = None) -> bool: + def write(self, name: str, data: bytes, mtime: float | None = None) -> bool: """Write a metadata entry. If mtime is specified, set it as the mtime of the entry. Otherwise, @@ -86,16 +86,16 @@ def getmtime(self, name: str) -> float: return int(os.path.getmtime(os.path.join(self.cache_dir_prefix, name))) - def read(self, name: str) -> str: + def read(self, name: str) -> bytes: assert os.path.normpath(name) != os.path.abspath(name), "Don't use absolute paths!" if not self.cache_dir_prefix: raise FileNotFoundError() - with open(os.path.join(self.cache_dir_prefix, name)) as f: + with open(os.path.join(self.cache_dir_prefix, name), "rb") as f: return f.read() - def write(self, name: str, data: str, mtime: float | None = None) -> bool: + def write(self, name: str, data: bytes, mtime: float | None = None) -> bool: assert os.path.normpath(name) != os.path.abspath(name), "Don't use absolute paths!" if not self.cache_dir_prefix: @@ -105,7 +105,7 @@ def write(self, name: str, data: str, mtime: float | None = None) -> bool: tmp_filename = path + "." + random_string() try: os.makedirs(os.path.dirname(path), exist_ok=True) - with open(tmp_filename, "w") as f: + with open(tmp_filename, "wb") as f: f.write(data) os.replace(tmp_filename, path) if mtime is not None: @@ -131,19 +131,17 @@ def list_all(self) -> Iterable[str]: for dir, _, files in os.walk(self.cache_dir_prefix): dir = os.path.relpath(dir, self.cache_dir_prefix) for file in files: - yield os.path.join(dir, file) + yield os.path.normpath(os.path.join(dir, file)) SCHEMA = """ -CREATE TABLE IF NOT EXISTS files ( +CREATE TABLE IF NOT EXISTS files2 ( path TEXT UNIQUE NOT NULL, mtime REAL, - data TEXT + data BLOB ); -CREATE INDEX IF NOT EXISTS path_idx on files(path); +CREATE INDEX IF NOT EXISTS path_idx on files2(path); """ -# No migrations yet -MIGRATIONS: list[str] = [] def connect_db(db_file: str) -> sqlite3.Connection: @@ -151,11 +149,6 @@ def connect_db(db_file: str) -> sqlite3.Connection: db = sqlite3.dbapi2.connect(db_file) db.executescript(SCHEMA) - for migr in MIGRATIONS: - try: - db.executescript(migr) - except sqlite3.OperationalError: - pass return db @@ -176,7 +169,7 @@ def _query(self, name: str, field: str) -> Any: if not self.db: raise FileNotFoundError() - cur = self.db.execute(f"SELECT {field} FROM files WHERE path = ?", (name,)) + cur = self.db.execute(f"SELECT {field} FROM files2 WHERE path = ?", (name,)) results = cur.fetchall() if not results: raise FileNotFoundError() @@ -188,12 +181,12 @@ def getmtime(self, name: str) -> float: assert isinstance(mtime, float) return mtime - def read(self, name: str) -> str: + def read(self, name: str) -> bytes: data = self._query(name, "data") - assert isinstance(data, str) + assert isinstance(data, bytes) return data - def write(self, name: str, data: str, mtime: float | None = None) -> bool: + def write(self, name: str, data: bytes, mtime: float | None = None) -> bool: import sqlite3 if not self.db: @@ -202,7 +195,7 @@ def write(self, name: str, data: str, mtime: float | None = None) -> bool: if mtime is None: mtime = time.time() self.db.execute( - "INSERT OR REPLACE INTO files(path, mtime, data) VALUES(?, ?, ?)", + "INSERT OR REPLACE INTO files2(path, mtime, data) VALUES(?, ?, ?)", (name, mtime, data), ) except sqlite3.OperationalError: @@ -213,7 +206,7 @@ def remove(self, name: str) -> None: if not self.db: raise FileNotFoundError() - self.db.execute("DELETE FROM files WHERE path = ?", (name,)) + self.db.execute("DELETE FROM files2 WHERE path = ?", (name,)) def commit(self) -> None: if self.db: @@ -221,5 +214,5 @@ def commit(self) -> None: def list_all(self) -> Iterable[str]: if self.db: - for row in self.db.execute("SELECT path FROM files"): + for row in self.db.execute("SELECT path FROM files2"): yield row[0] diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 452cfef20f4c7..fdd89837002f6 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -13,7 +13,7 @@ import subprocess import sys from enum import Enum, unique -from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union +from typing import Dict, Final, List, Optional, Tuple, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import pyinfo @@ -21,15 +21,35 @@ from mypy.fscache import FileSystemCache from mypy.nodes import MypyFile from mypy.options import Options -from mypy.stubinfo import approved_stub_package_exists +from mypy.stubinfo import stub_distribution_name +from mypy.util import os_path_join # Paths to be searched in find_module(). -class SearchPaths(NamedTuple): - python_path: tuple[str, ...] # where user code is found - mypy_path: tuple[str, ...] # from $MYPYPATH or config variable - package_path: tuple[str, ...] # from get_site_packages_dirs() - typeshed_path: tuple[str, ...] # paths in typeshed +class SearchPaths: + def __init__( + self, + python_path: tuple[str, ...], + mypy_path: tuple[str, ...], + package_path: tuple[str, ...], + typeshed_path: tuple[str, ...], + ) -> None: + # where user code is found + self.python_path = tuple(map(os.path.abspath, python_path)) + # from $MYPYPATH or config variable + self.mypy_path = tuple(map(os.path.abspath, mypy_path)) + # from get_site_packages_dirs() + self.package_path = tuple(map(os.path.abspath, package_path)) + # paths in typeshed + self.typeshed_path = tuple(map(os.path.abspath, typeshed_path)) + + def asdict(self) -> dict[str, tuple[str, ...]]: + return { + "python_path": self.python_path, + "mypy_path": self.mypy_path, + "package_path": self.package_path, + "typeshed_path": self.typeshed_path, + } # Package dirs are a two-tuple of path to search and whether to verify the module @@ -72,8 +92,7 @@ def error_message_templates(self, daemon: bool) -> tuple[str, list[str]]: elif self is ModuleNotFoundReason.WRONG_WORKING_DIRECTORY: msg = 'Cannot find implementation or library stub for module named "{module}"' notes = [ - "You may be running mypy in a subpackage, " - "mypy should be run on the package root" + "You may be running mypy in a subpackage, mypy should be run on the package root" ] elif self is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: msg = ( @@ -205,7 +224,7 @@ def find_module_via_source_set(self, id: str) -> ModuleSearchResult | None: d = os.path.dirname(p) for _ in range(id.count(".")): if not any( - self.fscache.isfile(os.path.join(d, "__init__" + x)) for x in PYTHON_EXTENSIONS + self.fscache.isfile(os_path_join(d, "__init__" + x)) for x in PYTHON_EXTENSIONS ): return None d = os.path.dirname(d) @@ -239,17 +258,17 @@ def find_module_via_source_set(self, id: str) -> ModuleSearchResult | None: return None def find_lib_path_dirs(self, id: str, lib_path: tuple[str, ...]) -> PackageDirs: - """Find which elements of a lib_path have the directory a module needs to exist. - - This is run for the python_path, mypy_path, and typeshed_path search paths. - """ + """Find which elements of a lib_path have the directory a module needs to exist.""" components = id.split(".") dir_chain = os.sep.join(components[:-1]) # e.g., 'foo/bar' dirs = [] for pathitem in self.get_toplevel_possibilities(lib_path, components[0]): # e.g., '/usr/lib/python3.4/foo/bar' - dir = os.path.normpath(os.path.join(pathitem, dir_chain)) + if dir_chain: + dir = os_path_join(pathitem, dir_chain) + else: + dir = pathitem if self.fscache.isdir(dir): dirs.append((dir, True)) return dirs @@ -315,13 +334,14 @@ def _typeshed_has_version(self, module: str) -> bool: return version >= min_version and (max_version is None or version <= max_version) def _find_module_non_stub_helper( - self, components: list[str], pkg_dir: str + self, id: str, pkg_dir: str ) -> OnePackageDir | ModuleNotFoundReason: plausible_match = False dir_path = pkg_dir + components = id.split(".") for index, component in enumerate(components): - dir_path = os.path.join(dir_path, component) - if self.fscache.isfile(os.path.join(dir_path, "py.typed")): + dir_path = os_path_join(dir_path, component) + if self.fscache.isfile(os_path_join(dir_path, "py.typed")): return os.path.join(pkg_dir, *components[:-1]), index == 0 elif not plausible_match and ( self.fscache.isdir(dir_path) or self.fscache.isfile(dir_path + ".py") @@ -330,10 +350,11 @@ def _find_module_non_stub_helper( # If this is not a directory then we can't traverse further into it if not self.fscache.isdir(dir_path): break - for i in range(len(components), 0, -1): - if approved_stub_package_exists(".".join(components[:i])): - return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED if plausible_match: + if self.options: + module_specific_options = self.options.clone_for_module(id) + if module_specific_options.follow_untyped_imports: + return os.path.join(pkg_dir, *components[:-1]), False return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS else: return ModuleNotFoundReason.NOT_FOUND @@ -414,13 +435,19 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: third_party_inline_dirs: PackageDirs = [] third_party_stubs_dirs: PackageDirs = [] found_possible_third_party_missing_type_hints = False - need_installed_stubs = False # Third-party stub/typed packages + candidate_package_dirs = { + package_dir[0] + for component in (components[0], components[0] + "-stubs") + for package_dir in self.find_lib_path_dirs(component, self.search_paths.package_path) + } for pkg_dir in self.search_paths.package_path: + if pkg_dir not in candidate_package_dirs: + continue stub_name = components[0] + "-stubs" - stub_dir = os.path.join(pkg_dir, stub_name) + stub_dir = os_path_join(pkg_dir, stub_name) if fscache.isdir(stub_dir): - stub_typed_file = os.path.join(stub_dir, "py.typed") + stub_typed_file = os_path_join(stub_dir, "py.typed") stub_components = [stub_name] + components[1:] path = os.path.join(pkg_dir, *stub_components[:-1]) if fscache.isdir(path): @@ -430,7 +457,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: # Partial here means that mypy should look at the runtime # package if installed. if fscache.read(stub_typed_file).decode().strip() == "partial": - runtime_path = os.path.join(pkg_dir, dir_chain) + runtime_path = os_path_join(pkg_dir, dir_chain) third_party_inline_dirs.append((runtime_path, True)) # if the package is partial, we don't verify the module, as # the partial stub package may not have a __init__.pyi @@ -441,15 +468,14 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: third_party_stubs_dirs.append((path, True)) else: third_party_stubs_dirs.append((path, True)) - non_stub_match = self._find_module_non_stub_helper(components, pkg_dir) + non_stub_match = self._find_module_non_stub_helper(id, pkg_dir) if isinstance(non_stub_match, ModuleNotFoundReason): if non_stub_match is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: found_possible_third_party_missing_type_hints = True - elif non_stub_match is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: - need_installed_stubs = True else: third_party_inline_dirs.append(non_stub_match) self._update_ns_ancestors(components, non_stub_match) + if self.options and self.options.use_builtins_fixtures: # Everything should be in fixtures. third_party_inline_dirs.clear() @@ -548,15 +574,28 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: if ancestor is not None: return ancestor - if need_installed_stubs: - return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED - elif found_possible_third_party_missing_type_hints: - return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS - else: + approved_dist_name = stub_distribution_name(id) + if approved_dist_name: + if len(components) == 1: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + # If we're a missing submodule of an already installed approved stubs, we don't want to + # error with APPROVED_STUBS_NOT_INSTALLED, but rather want to return NOT_FOUND. + for i in range(1, len(components)): + parent_id = ".".join(components[:i]) + if stub_distribution_name(parent_id) == approved_dist_name: + break + else: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED + if self.find_module(parent_id) is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED: + return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED return ModuleNotFoundReason.NOT_FOUND + if found_possible_third_party_missing_type_hints: + return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS + return ModuleNotFoundReason.NOT_FOUND + def find_modules_recursive(self, module: str) -> list[BuildSource]: - module_path = self.find_module(module) + module_path = self.find_module(module, fast_path=True) if isinstance(module_path, ModuleNotFoundReason): return [] sources = [BuildSource(module_path, module, None)] @@ -580,7 +619,7 @@ def find_modules_recursive(self, module: str) -> list[BuildSource]: # Skip certain names altogether if name in ("__pycache__", "site-packages", "node_modules") or name.startswith("."): continue - subpath = os.path.join(package_path, name) + subpath = os_path_join(package_path, name) if self.options and matches_exclude( subpath, self.options.exclude, self.fscache, self.options.verbosity >= 2 @@ -590,8 +629,8 @@ def find_modules_recursive(self, module: str) -> list[BuildSource]: if self.fscache.isdir(subpath): # Only recurse into packages if (self.options and self.options.namespace_packages) or ( - self.fscache.isfile(os.path.join(subpath, "__init__.py")) - or self.fscache.isfile(os.path.join(subpath, "__init__.pyi")) + self.fscache.isfile(os_path_join(subpath, "__init__.py")) + or self.fscache.isfile(os_path_join(subpath, "__init__.pyi")) ): seen.add(name) sources.extend(self.find_modules_recursive(module + "." + name)) @@ -636,7 +675,7 @@ def verify_module(fscache: FileSystemCache, id: str, path: str, prefix: str) -> for i in range(id.count(".")): path = os.path.dirname(path) if not any( - fscache.isfile_case(os.path.join(path, f"__init__{extension}"), prefix) + fscache.isfile_case(os_path_join(path, f"__init__{extension}"), prefix) for extension in PYTHON_EXTENSIONS ): return False @@ -651,7 +690,7 @@ def highest_init_level(fscache: FileSystemCache, id: str, path: str, prefix: str for i in range(id.count(".")): path = os.path.dirname(path) if any( - fscache.isfile_case(os.path.join(path, f"__init__{extension}"), prefix) + fscache.isfile_case(os_path_join(path, f"__init__{extension}"), prefix) for extension in PYTHON_EXTENSIONS ): level = i + 1 @@ -668,10 +707,13 @@ def mypy_path() -> list[str]: def default_lib_path( data_dir: str, pyversion: tuple[int, int], custom_typeshed_dir: str | None ) -> list[str]: - """Return default standard library search paths.""" + """Return default standard library search paths. Guaranteed to be normalised.""" + + data_dir = os.path.abspath(data_dir) path: list[str] = [] if custom_typeshed_dir: + custom_typeshed_dir = os.path.abspath(custom_typeshed_dir) typeshed_dir = os.path.join(custom_typeshed_dir, "stdlib") mypy_extensions_dir = os.path.join(custom_typeshed_dir, "stubs", "mypy-extensions") versions_file = os.path.join(typeshed_dir, "VERSIONS") @@ -711,7 +753,7 @@ def default_lib_path( @functools.lru_cache(maxsize=None) def get_search_dirs(python_executable: str | None) -> tuple[list[str], list[str]]: - """Find package directories for given python. + """Find package directories for given python. Guaranteed to return absolute paths. This runs a subprocess call, which generates a list of the directories in sys.path. To avoid repeatedly calling a subprocess (which can be slow!) we @@ -740,6 +782,7 @@ def get_search_dirs(python_executable: str | None) -> tuple[list[str], list[str] print(err.stdout) raise except OSError as err: + assert err.errno is not None reason = os.strerror(err.errno) raise CompileError( [f"mypy: Invalid python executable '{python_executable}': {reason}"] @@ -773,6 +816,7 @@ def compute_search_paths( root_dir = os.getenv("MYPY_TEST_PREFIX", None) if not root_dir: root_dir = os.path.dirname(os.path.dirname(__file__)) + root_dir = os.path.abspath(root_dir) lib_path.appendleft(os.path.join(root_dir, "test-data", "unit", "lib-stub")) # alt_lib_path is used by some tests to bypass the normal lib_path mechanics. # If we don't have one, grab directories of source files. @@ -842,11 +886,11 @@ def load_stdlib_py_versions(custom_typeshed_dir: str | None) -> StdlibVersions: None means there is no maximum version. """ - typeshed_dir = custom_typeshed_dir or os.path.join(os.path.dirname(__file__), "typeshed") - stdlib_dir = os.path.join(typeshed_dir, "stdlib") + typeshed_dir = custom_typeshed_dir or os_path_join(os.path.dirname(__file__), "typeshed") + stdlib_dir = os_path_join(typeshed_dir, "stdlib") result = {} - versions_path = os.path.join(stdlib_dir, "VERSIONS") + versions_path = os_path_join(stdlib_dir, "VERSIONS") assert os.path.isfile(versions_path), (custom_typeshed_dir, versions_path, __file__) with open(versions_path) as f: for line in f: diff --git a/mypy/nodes.py b/mypy/nodes.py index d215bcfce098a..9e26103e2f58c 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -561,17 +561,19 @@ class OverloadedFuncDef(FuncBase, SymbolNode, Statement): Overloaded variants must be consecutive in the source file. """ - __slots__ = ("items", "unanalyzed_items", "impl") + __slots__ = ("items", "unanalyzed_items", "impl", "deprecated") items: list[OverloadPart] unanalyzed_items: list[OverloadPart] impl: OverloadPart | None + deprecated: str | None def __init__(self, items: list[OverloadPart]) -> None: super().__init__() self.items = items self.unanalyzed_items = items.copy() self.impl = None + self.deprecated = None if items: # TODO: figure out how to reliably set end position (we don't know the impl here). self.set_line(items[0].line, items[0].column) @@ -596,6 +598,7 @@ def serialize(self) -> JsonDict: "fullname": self._fullname, "impl": None if self.impl is None else self.impl.serialize(), "flags": get_flags(self, FUNCBASE_FLAGS), + "deprecated": self.deprecated, } @classmethod @@ -615,6 +618,7 @@ def deserialize(cls, data: JsonDict) -> OverloadedFuncDef: res.type = typ res._fullname = data["fullname"] set_flags(res, data["flags"]) + res.deprecated = data["deprecated"] # NOTE: res.info will be set in the fixup phase. return res @@ -666,7 +670,7 @@ def set_line( class TypeParam: - __slots__ = ("name", "kind", "upper_bound", "values") + __slots__ = ("name", "kind", "upper_bound", "values", "default") def __init__( self, @@ -674,11 +678,13 @@ def __init__( kind: int, upper_bound: mypy.types.Type | None, values: list[mypy.types.Type], + default: mypy.types.Type | None, ) -> None: self.name = name self.kind = kind self.upper_bound = upper_bound self.values = values + self.default = default FUNCITEM_FLAGS: Final = FUNCBASE_FLAGS + [ @@ -778,9 +784,10 @@ class FuncDef(FuncItem, SymbolNode, Statement): "deco_line", "is_trivial_body", "is_mypy_only", - # Present only when a function is decorated with @typing.datasclass_transform or similar + # Present only when a function is decorated with @typing.dataclass_transform or similar "dataclass_transform_spec", "docstring", + "deprecated", ) __match_args__ = ("name", "arguments", "type", "body") @@ -810,6 +817,7 @@ def __init__( self.is_mypy_only = False self.dataclass_transform_spec: DataclassTransformSpec | None = None self.docstring: str | None = None + self.deprecated: str | None = None @property def name(self) -> str: @@ -840,6 +848,7 @@ def serialize(self) -> JsonDict: if self.dataclass_transform_spec is None else self.dataclass_transform_spec.serialize() ), + "deprecated": self.deprecated, } @classmethod @@ -867,6 +876,7 @@ def deserialize(cls, data: JsonDict) -> FuncDef: if data["dataclass_transform_spec"] is not None else None ) + ret.deprecated = data["deprecated"] # Leave these uninitialized so that future uses will trigger an error del ret.arguments del ret.max_pos @@ -959,6 +969,7 @@ def is_dynamic(self) -> bool: "is_classvar", "is_abstract_var", "is_final", + "is_index_var", "final_unset_in_class", "final_set_in_init", "explicit_self_type", @@ -995,6 +1006,7 @@ class Var(SymbolNode): "is_classvar", "is_abstract_var", "is_final", + "is_index_var", "final_unset_in_class", "final_set_in_init", "is_suppressed_import", @@ -1029,6 +1041,7 @@ def __init__(self, name: str, type: mypy.types.Type | None = None) -> None: self.is_settable_property = False self.is_classvar = False self.is_abstract_var = False + self.is_index_var = False # Set to true when this variable refers to a module we were unable to # parse for some reason (eg a silenced module) self.is_suppressed_import = False @@ -1250,7 +1263,7 @@ class Block(Statement): __match_args__ = ("body", "is_unreachable") - def __init__(self, body: list[Statement]) -> None: + def __init__(self, body: list[Statement], *, is_unreachable: bool = False) -> None: super().__init__() self.body = body # True if we can determine that this block is not executed during semantic @@ -1258,7 +1271,7 @@ def __init__(self, body: list[Statement]) -> None: # something like "if PY3:" when using Python 2. However, some code is # only considered unreachable during type checking and this is not true # in those cases. - self.is_unreachable = False + self.is_unreachable = is_unreachable def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_block(self) @@ -1649,7 +1662,7 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class TypeAliasStmt(Statement): - __slots__ = ("name", "type_args", "value", "invalid_recursive_alias") + __slots__ = ("name", "type_args", "value", "invalid_recursive_alias", "alias_node") __match_args__ = ("name", "type_args", "value") @@ -1657,6 +1670,7 @@ class TypeAliasStmt(Statement): type_args: list[TypeParam] value: LambdaExpr # Return value will get translated into a type invalid_recursive_alias: bool + alias_node: TypeAlias | None def __init__(self, name: NameExpr, type_args: list[TypeParam], value: LambdaExpr) -> None: super().__init__() @@ -1664,6 +1678,7 @@ def __init__(self, name: NameExpr, type_args: list[TypeParam], value: LambdaExpr self.type_args = type_args self.value = value self.invalid_recursive_alias = False + self.alias_node = None def accept(self, visitor: StatementVisitor[T]) -> T: return visitor.visit_type_alias_stmt(self) @@ -1712,7 +1727,7 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_str_expr(self) -def is_StrExpr_list(seq: list[Expression]) -> TypeGuard[list[StrExpr]]: +def is_StrExpr_list(seq: list[Expression]) -> TypeGuard[list[StrExpr]]: # noqa: N802 return all(isinstance(item, StrExpr) for item in seq) @@ -2887,6 +2902,10 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: } ) +# Attributes that can optionally be defined in the body of a subclass of +# enum.Enum but are removed from the class __dict__ by EnumMeta. +EXCLUDED_ENUM_ATTRIBUTES: Final = frozenset({"_ignore_", "_order_", "__order__"}) + class TypeInfo(SymbolNode): """The type structure of a single class. @@ -2942,6 +2961,7 @@ class is generic then it will be a type constructor of higher kind. "self_type", "dataclass_transform_spec", "is_type_check_only", + "deprecated", ) _fullname: str # Fully qualified name @@ -3095,6 +3115,9 @@ class is generic then it will be a type constructor of higher kind. # Is set to `True` when class is decorated with `@typing.type_check_only` is_type_check_only: bool + # The type's deprecation message (in case it is deprecated) + deprecated: str | None + FLAGS: Final = [ "is_abstract", "is_enum", @@ -3152,6 +3175,7 @@ def __init__(self, names: SymbolTable, defn: ClassDef, module_name: str) -> None self.self_type = None self.dataclass_transform_spec = None self.is_type_check_only = False + self.deprecated = None def add_type_vars(self) -> None: self.has_type_var_tuple_type = False @@ -3165,9 +3189,6 @@ def add_type_vars(self) -> None: self.type_var_tuple_prefix = i self.type_var_tuple_suffix = len(self.defn.type_vars) - i - 1 self.type_vars.append(vd.name) - assert not ( - self.has_param_spec_type and self.has_type_var_tuple_type - ), "Mixing type var tuples and param specs not supported yet" @property def name(self) -> str: @@ -3212,6 +3233,19 @@ def protocol_members(self) -> list[str]: members.add(name) return sorted(members) + @property + def enum_members(self) -> list[str]: + return [ + name + for name, sym in self.names.items() + if ( + isinstance(sym.node, Var) + and name not in EXCLUDED_ENUM_ATTRIBUTES + and not name.startswith("__") + and sym.node.has_explicit_value + ) + ] + def __getitem__(self, name: str) -> SymbolTableNode: n = self.get(name) if n: @@ -3377,6 +3411,7 @@ def serialize(self) -> JsonDict: if self.dataclass_transform_spec is not None else None ), + "deprecated": self.deprecated, } return data @@ -3444,6 +3479,7 @@ def deserialize(cls, data: JsonDict) -> TypeInfo: ti.dataclass_transform_spec = DataclassTransformSpec.deserialize( data["dataclass_transform_spec"] ) + ti.deprecated = data.get("deprecated") return ti @@ -3483,6 +3519,7 @@ def __getattribute__(self, attr: str) -> type: VAR_NO_INFO: Final[TypeInfo] = FakeInfo("Var is lacking info") CLASSDEF_NO_INFO: Final[TypeInfo] = FakeInfo("ClassDef is lacking info") FUNC_NO_INFO: Final[TypeInfo] = FakeInfo("FuncBase for non-methods lack info") +MISSING_FALLBACK: Final = FakeInfo("fallback can't be filled out until semanal") class TypeAlias(SymbolNode): @@ -4092,7 +4129,7 @@ def get_member_expr_fullname(expr: MemberExpr) -> str | None: initial = expr.expr.name elif isinstance(expr.expr, MemberExpr): initial = get_member_expr_fullname(expr.expr) - else: + if initial is None: return None return f"{initial}.{expr.name}" diff --git a/mypy/options.py b/mypy/options.py index 5ef6bc2a35e71..33a2c75d164e7 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -42,6 +42,7 @@ class BuildType: "extra_checks", "follow_imports_for_stubs", "follow_imports", + "follow_untyped_imports", "ignore_errors", "ignore_missing_imports", "implicit_optional", @@ -74,8 +75,9 @@ class BuildType: UNPACK: Final = "Unpack" PRECISE_TUPLE_TYPES: Final = "PreciseTupleTypes" NEW_GENERIC_SYNTAX: Final = "NewGenericSyntax" -INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, NEW_GENERIC_SYNTAX)) -COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK)) +INLINE_TYPEDDICT: Final = "InlineTypedDict" +INCOMPLETE_FEATURES: Final = frozenset((PRECISE_TUPLE_TYPES, INLINE_TYPEDDICT)) +COMPLETE_FEATURES: Final = frozenset((TYPE_VAR_TUPLE, UNPACK, NEW_GENERIC_SYNTAX)) class Options: @@ -112,6 +114,8 @@ def __init__(self) -> None: self.ignore_missing_imports = False # Is ignore_missing_imports set in a per-module section self.ignore_missing_imports_per_module = False + # Typecheck modules without stubs or py.typed marker + self.follow_untyped_imports = False self.follow_imports = "normal" # normal|silent|skip|error # Whether to respect the follow_imports setting even for stub files. # Intended to be used for disabling specific stubs. @@ -172,6 +176,9 @@ def __init__(self) -> None: # declared with a precise type self.warn_return_any = False + # Report importing or using deprecated features as errors instead of notes. + self.report_deprecated_as_note = False + # Warn about unused '# type: ignore' comments self.warn_unused_ignores = False @@ -396,17 +403,12 @@ def use_or_syntax(self) -> bool: def use_star_unpack(self) -> bool: return self.python_version >= (3, 11) - # To avoid breaking plugin compatibility, keep providing new_semantic_analyzer - @property - def new_semantic_analyzer(self) -> bool: - return True - def snapshot(self) -> dict[str, object]: """Produce a comparable snapshot of this Option""" # Under mypyc, we don't have a __dict__, so we need to do worse things. d = dict(getattr(self, "__dict__", ())) for k in get_class_descriptors(Options): - if hasattr(self, k) and k != "new_semantic_analyzer": + if hasattr(self, k): d[k] = getattr(self, k) # Remove private attributes from snapshot d = {k: v for k, v in d.items() if not k.startswith("_")} @@ -415,6 +417,33 @@ def snapshot(self) -> dict[str, object]: def __repr__(self) -> str: return f"Options({pprint.pformat(self.snapshot())})" + def process_error_codes(self, *, error_callback: Callable[[str], Any]) -> None: + # Process `--enable-error-code` and `--disable-error-code` flags + disabled_codes = set(self.disable_error_code) + enabled_codes = set(self.enable_error_code) + + valid_error_codes = set(error_codes.keys()) + + invalid_codes = (enabled_codes | disabled_codes) - valid_error_codes + if invalid_codes: + error_callback(f"Invalid error code(s): {', '.join(sorted(invalid_codes))}") + + self.disabled_error_codes |= {error_codes[code] for code in disabled_codes} + self.enabled_error_codes |= {error_codes[code] for code in enabled_codes} + + # Enabling an error code always overrides disabling + self.disabled_error_codes -= self.enabled_error_codes + + def process_incomplete_features( + self, *, error_callback: Callable[[str], Any], warning_callback: Callable[[str], Any] + ) -> None: + # Validate incomplete features. + for feature in self.enable_incomplete_feature: + if feature not in INCOMPLETE_FEATURES | COMPLETE_FEATURES: + error_callback(f"Unknown incomplete feature: {feature}") + if feature in COMPLETE_FEATURES: + warning_callback(f"Warning: {feature} is already enabled by default") + def apply_changes(self, changes: dict[str, object]) -> Options: # Note: effects of this method *must* be idempotent. new_options = Options() diff --git a/mypy/plugin.py b/mypy/plugin.py index 858795addb7f8..fcbbc32f62374 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -114,9 +114,6 @@ class C: pass Note that a forward reference in a function signature won't trigger another pass, since all functions are processed only after the top level has been fully analyzed. - -You can use `api.options.new_semantic_analyzer` to check whether the new -semantic analyzer is enabled (it's always true in mypy 0.730 and later). """ from __future__ import annotations @@ -240,7 +237,7 @@ def type_context(self) -> list[Type | None]: @abstractmethod def fail( - self, msg: str | ErrorMessage, ctx: Context, *, code: ErrorCode | None = None + self, msg: str | ErrorMessage, ctx: Context, /, *, code: ErrorCode | None = None ) -> None: """Emit an error message at given location.""" raise NotImplementedError @@ -495,6 +492,7 @@ class MethodContext(NamedTuple): class AttributeContext(NamedTuple): type: ProperType # Type of object with attribute default_attr_type: Type # Original attribute type + is_lvalue: bool # Whether the attribute is the target of an assignment context: Context # Relevant location context (e.g. for error messages) api: CheckerPluginInterface diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index db976385ee56e..b67a285af11da 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -55,6 +55,7 @@ deserialize_and_fixup_type, ) from mypy.server.trigger import make_wildcard_trigger +from mypy.state import state from mypy.typeops import get_type_vars, make_simplified_union, map_type_from_supertype from mypy.types import ( AnyType, @@ -317,9 +318,23 @@ def attr_class_maker_callback( See https://www.attrs.org/en/stable/how-does-it-work.html for information on how attrs works. - If this returns False, some required metadata was not ready yet and we need another + If this returns False, some required metadata was not ready yet, and we need another pass. """ + with state.strict_optional_set(ctx.api.options.strict_optional): + # This hook is called during semantic analysis, but it uses a bunch of + # type-checking ops, so it needs the strict optional set properly. + return attr_class_maker_callback_impl( + ctx, auto_attribs_default, frozen_default, slots_default + ) + + +def attr_class_maker_callback_impl( + ctx: mypy.plugin.ClassDefContext, + auto_attribs_default: bool | None, + frozen_default: bool, + slots_default: bool, +) -> bool: info = ctx.cls.info init = _get_decorator_bool_argument(ctx, "init", True) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index dd2eceab217f7..349eca7f01436 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -385,6 +385,9 @@ def transform(self) -> bool: self._add_dataclass_fields_magic_attribute() self._add_internal_replace_method(attributes) + if self._api.options.python_version >= (3, 13): + self._add_dunder_replace(attributes) + if "__post_init__" in info.names: self._add_internal_post_init_method(attributes) @@ -395,6 +398,22 @@ def transform(self) -> bool: return True + def _add_dunder_replace(self, attributes: list[DataclassAttribute]) -> None: + """Add a `__replace__` method to the class, which is used to replace attributes in the `copy` module.""" + args = [ + attr.to_argument(self._cls.info, of="replace") + for attr in attributes + if attr.is_in_init + ] + type_vars = [tv for tv in self._cls.type_vars] + add_method_to_class( + self._api, + self._cls, + "__replace__", + args=args, + return_type=Instance(self._cls.info, type_vars), + ) + def _add_internal_replace_method(self, attributes: list[DataclassAttribute]) -> None: """ Stashes the signature of 'dataclasses.replace(...)' for this specific dataclass @@ -429,8 +448,7 @@ def add_slots( # This means that version is lower than `3.10`, # it is just a non-existent argument for `dataclass` function. self._api.fail( - 'Keyword argument "slots" for "dataclass" ' - "is only valid in Python 3.10 and higher", + 'Keyword argument "slots" for "dataclass" is only valid in Python 3.10 and higher', self._reason, ) return diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 5139b9b82289a..73c5742614eef 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -1,7 +1,7 @@ from __future__ import annotations from functools import partial -from typing import Callable +from typing import Callable, Final import mypy.errorcodes as codes from mypy import message_registry @@ -372,6 +372,10 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: ) return AnyType(TypeOfAny.from_error) + assigned_readonly_keys = ctx.type.readonly_keys & set(keys) + if assigned_readonly_keys: + ctx.api.msg.readonly_keys_mutated(assigned_readonly_keys, context=ctx.context) + default_type = ctx.arg_types[1][0] value_types = [] @@ -415,13 +419,16 @@ def typed_dict_delitem_callback(ctx: MethodContext) -> Type: return AnyType(TypeOfAny.from_error) for key in keys: - if key in ctx.type.required_keys: + if key in ctx.type.required_keys or key in ctx.type.readonly_keys: ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, ctx.context) elif key not in ctx.type.items: ctx.api.msg.typeddict_key_not_found(ctx.type, key, ctx.context) return ctx.default_return_type +_TP_DICT_MUTATING_METHODS: Final = frozenset({"update of TypedDict", "__ior__ of TypedDict"}) + + def typed_dict_update_signature_callback(ctx: MethodSigContext) -> CallableType: """Try to infer a better signature type for methods that update `TypedDict`. @@ -436,10 +443,19 @@ def typed_dict_update_signature_callback(ctx: MethodSigContext) -> CallableType: arg_type = arg_type.as_anonymous() arg_type = arg_type.copy_modified(required_keys=set()) if ctx.args and ctx.args[0]: - with ctx.api.msg.filter_errors(): + if signature.name in _TP_DICT_MUTATING_METHODS: + # If we want to mutate this object in place, we need to set this flag, + # it will trigger an extra check in TypedDict's checker. + arg_type.to_be_mutated = True + with ctx.api.msg.filter_errors( + filter_errors=lambda name, info: info.code != codes.TYPEDDICT_READONLY_MUTATED, + save_filtered_errors=True, + ): inferred = get_proper_type( ctx.api.get_expression_type(ctx.args[0][0], type_context=arg_type) ) + if arg_type.to_be_mutated: + arg_type.to_be_mutated = False # Done! possible_tds = [] if isinstance(inferred, TypedDictType): possible_tds = [inferred] diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 9589c6aeca8be..6a063174bfcba 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -8,16 +8,21 @@ import mypy.plugin import mypy.semanal from mypy.argmap import map_actuals_to_formals -from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, Var +from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, NameExpr, Var from mypy.plugins.common import add_method_to_class +from mypy.typeops import get_all_type_vars from mypy.types import ( AnyType, CallableType, Instance, Overloaded, + ParamSpecFlavor, + ParamSpecType, Type, TypeOfAny, + TypeVarType, UnboundType, + UnionType, get_proper_type, ) @@ -130,7 +135,19 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: if isinstance(get_proper_type(ctx.arg_types[0][0]), Overloaded): # TODO: handle overloads, just fall back to whatever the non-plugin code does return ctx.default_return_type - fn_type = ctx.api.extract_callable_type(ctx.arg_types[0][0], ctx=ctx.default_return_type) + return handle_partial_with_callee(ctx, callee=ctx.arg_types[0][0]) + + +def handle_partial_with_callee(ctx: mypy.plugin.FunctionContext, callee: Type) -> Type: + if not isinstance(ctx.api, mypy.checker.TypeChecker): # use internals + return ctx.default_return_type + + if isinstance(callee_proper := get_proper_type(callee), UnionType): + return UnionType.make_union( + [handle_partial_with_callee(ctx, item) for item in callee_proper.items] + ) + + fn_type = ctx.api.extract_callable_type(callee, ctx=ctx.default_return_type) if fn_type is None: return ctx.default_return_type @@ -151,21 +168,6 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: ctx.api.type_context[-1] = None wrapped_return = False - defaulted = fn_type.copy_modified( - arg_kinds=[ - ( - ArgKind.ARG_OPT - if k == ArgKind.ARG_POS - else (ArgKind.ARG_NAMED_OPT if k == ArgKind.ARG_NAMED else k) - ) - for k in fn_type.arg_kinds - ], - ret_type=ret_type, - ) - if defaulted.line < 0: - # Make up a line number if we don't have one - defaulted.set_line(ctx.default_return_type) - # Flatten actual to formal mapping, since this is what check_call() expects. actual_args = [] actual_arg_kinds = [] @@ -186,6 +188,45 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: actual_arg_names.append(ctx.arg_names[i][j]) actual_types.append(ctx.arg_types[i][j]) + formal_to_actual = map_actuals_to_formals( + actual_kinds=actual_arg_kinds, + actual_names=actual_arg_names, + formal_kinds=fn_type.arg_kinds, + formal_names=fn_type.arg_names, + actual_arg_type=lambda i: actual_types[i], + ) + + # We need to remove any type variables that appear only in formals that have + # no actuals, to avoid eagerly binding them in check_call() below. + can_infer_ids = set() + for i, arg_type in enumerate(fn_type.arg_types): + if not formal_to_actual[i]: + continue + can_infer_ids.update({tv.id for tv in get_all_type_vars(arg_type)}) + + # special_sig="partial" allows omission of args/kwargs typed with ParamSpec + defaulted = fn_type.copy_modified( + arg_kinds=[ + ( + ArgKind.ARG_OPT + if k == ArgKind.ARG_POS + else (ArgKind.ARG_NAMED_OPT if k == ArgKind.ARG_NAMED else k) + ) + for k in fn_type.arg_kinds + ], + ret_type=ret_type, + variables=[ + tv + for tv in fn_type.variables + # Keep TypeVarTuple/ParamSpec to avoid spurious errors on empty args. + if tv.id in can_infer_ids or not isinstance(tv, TypeVarType) + ], + special_sig="partial", + ) + if defaulted.line < 0: + # Make up a line number if we don't have one + defaulted.set_line(ctx.default_return_type) + # Create a valid context for various ad-hoc inspections in check_call(). call_expr = CallExpr( callee=ctx.args[0][0], @@ -218,14 +259,6 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: return ctx.default_return_type bound = bound.copy_modified(ret_type=ret_type.args[0]) - formal_to_actual = map_actuals_to_formals( - actual_kinds=actual_arg_kinds, - actual_names=actual_arg_names, - formal_kinds=fn_type.arg_kinds, - formal_names=fn_type.arg_names, - actual_arg_type=lambda i: actual_types[i], - ) - partial_kinds = [] partial_types = [] partial_names = [] @@ -245,11 +278,14 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: partial_kinds.append(fn_type.arg_kinds[i]) partial_types.append(arg_type) partial_names.append(fn_type.arg_names[i]) - elif actuals: - if any(actual_arg_kinds[j] == ArgKind.ARG_POS for j in actuals): + else: + assert actuals + if any(actual_arg_kinds[j] in (ArgKind.ARG_POS, ArgKind.ARG_STAR) for j in actuals): + # Don't add params for arguments passed positionally continue + # Add defaulted params for arguments passed via keyword kind = actual_arg_kinds[actuals[0]] - if kind == ArgKind.ARG_NAMED: + if kind == ArgKind.ARG_NAMED or kind == ArgKind.ARG_STAR2: kind = ArgKind.ARG_NAMED_OPT partial_kinds.append(kind) partial_types.append(arg_type) @@ -264,10 +300,19 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: arg_kinds=partial_kinds, arg_names=partial_names, ret_type=ret_type, + special_sig="partial", ) ret = ctx.api.named_generic_type(PARTIAL, [ret_type]) ret = ret.copy_with_extra_attr("__mypy_partial", partially_applied) + if partially_applied.param_spec(): + assert ret.extra_attrs is not None # copy_with_extra_attr above ensures this + attrs = ret.extra_attrs.copy() + if ArgKind.ARG_STAR in actual_arg_kinds: + attrs.immutable.add("__mypy_partial_paramspec_args_bound") + if ArgKind.ARG_STAR2 in actual_arg_kinds: + attrs.immutable.add("__mypy_partial_paramspec_kwargs_bound") + ret.extra_attrs = attrs return ret @@ -282,19 +327,55 @@ def partial_call_callback(ctx: mypy.plugin.MethodContext) -> Type: ): return ctx.default_return_type - partial_type = ctx.type.extra_attrs.attrs["__mypy_partial"] + extra_attrs = ctx.type.extra_attrs + partial_type = get_proper_type(extra_attrs.attrs["__mypy_partial"]) if len(ctx.arg_types) != 2: # *args, **kwargs return ctx.default_return_type - args = [a for param in ctx.args for a in param] - arg_kinds = [a for param in ctx.arg_kinds for a in param] - arg_names = [a for param in ctx.arg_names for a in param] + # See comments for similar actual to formal code above + actual_args = [] + actual_arg_kinds = [] + actual_arg_names = [] + seen_args = set() + for i, param in enumerate(ctx.args): + for j, a in enumerate(param): + if a in seen_args: + continue + seen_args.add(a) + actual_args.append(a) + actual_arg_kinds.append(ctx.arg_kinds[i][j]) + actual_arg_names.append(ctx.arg_names[i][j]) - result = ctx.api.expr_checker.check_call( + result, _ = ctx.api.expr_checker.check_call( callee=partial_type, - args=args, - arg_kinds=arg_kinds, - arg_names=arg_names, + args=actual_args, + arg_kinds=actual_arg_kinds, + arg_names=actual_arg_names, context=ctx.context, ) - return result[0] + if not isinstance(partial_type, CallableType) or partial_type.param_spec() is None: + return result + + args_bound = "__mypy_partial_paramspec_args_bound" in extra_attrs.immutable + kwargs_bound = "__mypy_partial_paramspec_kwargs_bound" in extra_attrs.immutable + + passed_paramspec_parts = [ + arg.node.type + for arg in actual_args + if isinstance(arg, NameExpr) + and isinstance(arg.node, Var) + and isinstance(arg.node.type, ParamSpecType) + ] + # ensure *args: P.args + args_passed = any(part.flavor == ParamSpecFlavor.ARGS for part in passed_paramspec_parts) + if not args_bound and not args_passed: + ctx.api.expr_checker.msg.too_few_arguments(partial_type, ctx.context, actual_arg_names) + elif args_bound and args_passed: + ctx.api.expr_checker.msg.too_many_arguments(partial_type, ctx.context) + + # ensure **kwargs: P.kwargs + kwargs_passed = any(part.flavor == ParamSpecFlavor.KWARGS for part in passed_paramspec_parts) + if not kwargs_bound and not kwargs_passed: + ctx.api.expr_checker.msg.too_few_arguments(partial_type, ctx.context, actual_arg_names) + + return result diff --git a/mypy/plugins/proper_plugin.py b/mypy/plugins/proper_plugin.py index a1fd05272b655..f51685c80afad 100644 --- a/mypy/plugins/proper_plugin.py +++ b/mypy/plugins/proper_plugin.py @@ -106,6 +106,7 @@ def is_special_target(right: ProperType) -> bool: "mypy.types.ErasedType", "mypy.types.DeletedType", "mypy.types.RequiredType", + "mypy.types.ReadOnlyType", ): # Special case: these are not valid targets for a type alias and thus safe. # TODO: introduce a SyntheticType base to simplify this? diff --git a/mypy/report.py b/mypy/report.py index 764cfec7799ac..73942b6c5ae3e 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -689,6 +689,8 @@ def on_finish(self) -> None: self.root_package.covered_lines, self.root_package.total_lines ) self.root.attrib["branch-rate"] = "0" + self.root.attrib["lines-covered"] = str(self.root_package.covered_lines) + self.root.attrib["lines-valid"] = str(self.root_package.total_lines) sources = etree.SubElement(self.root, "sources") source_element = etree.SubElement(sources, "source") source_element.text = os.getcwd() diff --git a/mypy/semanal.py b/mypy/semanal.py index f857c3e733819..edcc50e66e307 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -249,6 +249,7 @@ from mypy.types import ( ASSERT_TYPE_NAMES, DATACLASS_TRANSFORM_NAMES, + DEPRECATED_TYPE_NAMES, FINAL_DECORATOR_NAMES, FINAL_TYPE_NAMES, IMPORTED_REVEAL_TYPE_NAMES, @@ -289,6 +290,7 @@ UnpackType, get_proper_type, get_proper_types, + has_type_vars, is_named_instance, remove_dups, type_vars_as_args, @@ -483,6 +485,12 @@ def __init__( # Used to pass information about current overload index to visit_func_def(). self.current_overload_item: int | None = None + # Used to track whether currently inside an except* block. This helps + # to invoke errors when continue/break/return is used inside except* block. + self.inside_except_star_block: bool = False + # Used to track edge case when return is still inside except* if it enters a loop + self.return_stmt_inside_except_star_block: bool = False + # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make them properties @property @@ -510,6 +518,25 @@ def allow_unbound_tvars_set(self) -> Iterator[None]: finally: self.allow_unbound_tvars = old + @contextmanager + def inside_except_star_block_set( + self, value: bool, entering_loop: bool = False + ) -> Iterator[None]: + old = self.inside_except_star_block + self.inside_except_star_block = value + + # Return statement would still be in except* scope if entering loops + if not entering_loop: + old_return_stmt_flag = self.return_stmt_inside_except_star_block + self.return_stmt_inside_except_star_block = value + + try: + yield + finally: + self.inside_except_star_block = old + if not entering_loop: + self.return_stmt_inside_except_star_block = old_return_stmt_flag + # # Preparing module (performed before semantic analysis) # @@ -824,6 +851,11 @@ def file_context( self.num_incomplete_refs = 0 if active_type: + enclosing_fullname = active_type.fullname.rsplit(".", 1)[0] + if "." in enclosing_fullname: + enclosing_node = self.lookup_fully_qualified_or_none(enclosing_fullname) + if enclosing_node and isinstance(enclosing_node.node, TypeInfo): + self._type = enclosing_node.node self.push_type_args(active_type.defn.type_args, active_type.defn) self.incomplete_type_stack.append(False) scope.enter_class(active_type) @@ -871,7 +903,8 @@ def visit_func_def(self, defn: FuncDef) -> None: return with self.scope.function_scope(defn): - self.analyze_func_def(defn) + with self.inside_except_star_block_set(value=False): + self.analyze_func_def(defn) def function_fullname(self, fullname: str) -> str: if self.current_overload_item is None: @@ -1255,10 +1288,51 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: return # We know this is an overload def. Infer properties and perform some checks. + self.process_deprecated_overload(defn) self.process_final_in_overload(defn) self.process_static_or_class_method_in_overload(defn) self.process_overload_impl(defn) + def process_deprecated_overload(self, defn: OverloadedFuncDef) -> None: + if defn.is_property: + return + + if isinstance(impl := defn.impl, Decorator) and ( + (deprecated := impl.func.deprecated) is not None + ): + defn.deprecated = deprecated + for item in defn.items: + if isinstance(item, Decorator): + item.func.deprecated = deprecated + + for item in defn.items: + deprecation = False + if isinstance(item, Decorator): + for d in item.decorators: + if deprecation and refers_to_fullname(d, OVERLOAD_NAMES): + self.msg.note("@overload should be placed before @deprecated", d) + elif (deprecated := self.get_deprecated(d)) is not None: + deprecation = True + if isinstance(typ := item.func.type, CallableType): + typestr = f" {typ} " + else: + typestr = " " + item.func.deprecated = ( + f"overload{typestr}of function {defn.fullname} is deprecated: " + f"{deprecated}" + ) + + @staticmethod + def get_deprecated(expression: Expression) -> str | None: + if ( + isinstance(expression, CallExpr) + and refers_to_fullname(expression.callee, DEPRECATED_TYPE_NAMES) + and (len(args := expression.args) >= 1) + and isinstance(deprecated := args[0], StrExpr) + ): + return deprecated.value + return None + def process_overload_impl(self, defn: OverloadedFuncDef) -> None: """Set flags for an overload implementation. @@ -1435,15 +1509,17 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - for i, item in enumerate(items[1:]): if isinstance(item, Decorator): if len(item.decorators) >= 1: - node = item.decorators[0] - if isinstance(node, MemberExpr): - if node.name == "setter": + first_node = item.decorators[0] + if isinstance(first_node, MemberExpr): + if first_node.name == "setter": # The first item represents the entire property. first_item.var.is_settable_property = True # Get abstractness from the original definition. item.func.abstract_status = first_item.func.abstract_status - if node.name == "deleter": + if first_node.name == "deleter": item.func.abstract_status = first_item.func.abstract_status + for other_node in item.decorators[1:]: + other_node.accept(self) else: self.fail( f"Only supported top decorator is @{first_item.func.name}.setter", item @@ -1455,6 +1531,14 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - for i in reversed(deleted_items): del items[i] + for item in items[1:]: + if isinstance(item, Decorator): + for d in item.decorators: + if (deprecated := self.get_deprecated(d)) is not None: + item.func.deprecated = ( + f"function {item.fullname} is deprecated: {deprecated}" + ) + def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> None: if self.is_class_scope(): assert self.type is not None @@ -1627,6 +1711,7 @@ def visit_decorator(self, dec: Decorator) -> None: "abc.abstractproperty", "functools.cached_property", "enum.property", + "types.DynamicClassAttribute", ), ): removed.append(i) @@ -1658,6 +1743,8 @@ def visit_decorator(self, dec: Decorator) -> None: d.callee, DATACLASS_TRANSFORM_NAMES ): dec.func.dataclass_transform_spec = self.parse_dataclass_transform_spec(d) + elif (deprecated := self.get_deprecated(d)) is not None: + dec.func.deprecated = f"function {dec.fullname} is deprecated: {deprecated}" elif not dec.var.is_property: # We have seen a "non-trivial" decorator before seeing @property, if # we will see a @property later, give an error, as we don't support this. @@ -1749,15 +1836,38 @@ def analyze_type_param( upper_bound = self.named_type("builtins.tuple", [self.object_type()]) else: upper_bound = self.object_type() - default = AnyType(TypeOfAny.from_omitted_generics) + if type_param.default: + default = self.anal_type( + type_param.default, + allow_placeholder=True, + allow_unbound_tvars=True, + report_invalid_types=False, + allow_param_spec_literals=type_param.kind == PARAM_SPEC_KIND, + allow_tuple_literal=type_param.kind == PARAM_SPEC_KIND, + allow_unpack=type_param.kind == TYPE_VAR_TUPLE_KIND, + ) + if default is None: + default = PlaceholderType(None, [], context.line) + elif type_param.kind == TYPE_VAR_KIND: + default = self.check_typevar_default(default, type_param.default) + elif type_param.kind == PARAM_SPEC_KIND: + default = self.check_paramspec_default(default, type_param.default) + elif type_param.kind == TYPE_VAR_TUPLE_KIND: + default = self.check_typevartuple_default(default, type_param.default) + else: + default = AnyType(TypeOfAny.from_omitted_generics) if type_param.kind == TYPE_VAR_KIND: - values = [] + values: list[Type] = [] if type_param.values: for value in type_param.values: analyzed = self.anal_type(value, allow_placeholder=True) if analyzed is None: analyzed = PlaceholderType(None, [], context.line) - values.append(analyzed) + if has_type_vars(analyzed): + self.fail(message_registry.TYPE_VAR_GENERIC_CONSTRAINT_TYPE, context) + values.append(AnyType(TypeOfAny.from_error)) + else: + values.append(analyzed) return TypeVarExpr( name=type_param.name, fullname=fullname, @@ -1819,6 +1929,8 @@ def analyze_class(self, defn: ClassDef) -> None: defn, bases, context=defn ) + self.check_type_alias_bases(bases) + for tvd in tvar_defs: if isinstance(tvd, TypeVarType) and any( has_placeholder(t) for t in [tvd.upper_bound] + tvd.values @@ -1890,6 +2002,19 @@ def analyze_class(self, defn: ClassDef) -> None: self.analyze_class_body_common(defn) + def check_type_alias_bases(self, bases: list[Expression]) -> None: + for base in bases: + if isinstance(base, IndexExpr): + base = base.base + if ( + isinstance(base, RefExpr) + and isinstance(base.node, TypeAlias) + and base.node.python_3_12_type_alias + ): + self.fail( + 'Type alias defined using "type" statement not valid as base class', base + ) + def setup_type_vars(self, defn: ClassDef, tvar_defs: list[TypeVarLikeType]) -> None: defn.type_vars = tvar_defs defn.info.type_vars = [] @@ -2079,6 +2204,8 @@ def analyze_class_decorator_common( info.is_final = True elif refers_to_fullname(decorator, TYPE_CHECK_ONLY_NAMES): info.is_type_check_only = True + elif (deprecated := self.get_deprecated(decorator)) is not None: + info.deprecated = f"class {defn.fullname} is deprecated: {deprecated}" def clean_up_bases_and_infer_type_variables( self, defn: ClassDef, base_type_exprs: list[Expression], context: Context @@ -2167,21 +2294,7 @@ class Foo(Bar, Generic[T]): ... # grained incremental mode. defn.removed_base_type_exprs.append(defn.base_type_exprs[i]) del base_type_exprs[i] - tvar_defs: list[TypeVarLikeType] = [] - last_tvar_name_with_default: str | None = None - for name, tvar_expr in declared_tvars: - tvar_expr.default = tvar_expr.default.accept( - TypeVarDefaultTranslator(self, tvar_expr.name, context) - ) - tvar_def = self.tvar_scope.bind_new(name, tvar_expr) - if last_tvar_name_with_default is not None and not tvar_def.has_default(): - self.msg.tvar_without_default_type( - tvar_def.name, last_tvar_name_with_default, context - ) - tvar_def.default = AnyType(TypeOfAny.from_error) - elif tvar_def.has_default(): - last_tvar_name_with_default = tvar_def.name - tvar_defs.append(tvar_def) + tvar_defs = self.tvar_defs_from_tvars(declared_tvars, context) return base_type_exprs, tvar_defs, is_protocol def analyze_class_typevar_declaration(self, base: Type) -> tuple[TypeVarLikeList, bool] | None: @@ -2282,6 +2395,26 @@ def get_all_bases_tvars( tvars.extend(base_tvars) return remove_dups(tvars) + def tvar_defs_from_tvars( + self, tvars: TypeVarLikeList, context: Context + ) -> list[TypeVarLikeType]: + tvar_defs: list[TypeVarLikeType] = [] + last_tvar_name_with_default: str | None = None + for name, tvar_expr in tvars: + tvar_expr.default = tvar_expr.default.accept( + TypeVarDefaultTranslator(self, tvar_expr.name, context) + ) + tvar_def = self.tvar_scope.bind_new(name, tvar_expr) + if last_tvar_name_with_default is not None and not tvar_def.has_default(): + self.msg.tvar_without_default_type( + tvar_def.name, last_tvar_name_with_default, context + ) + tvar_def.default = AnyType(TypeOfAny.from_error) + elif tvar_def.has_default(): + last_tvar_name_with_default = tvar_def.name + tvar_defs.append(tvar_def) + return tvar_defs + def get_and_bind_all_tvars(self, type_exprs: list[Expression]) -> list[TypeVarLikeType]: """Return all type variable references in item type expressions. @@ -2535,8 +2668,7 @@ def calculate_class_mro( calculate_mro(defn.info, obj_type) except MroError: self.fail( - "Cannot determine consistent method resolution " - 'order (MRO) for "%s"' % defn.name, + f'Cannot determine consistent method resolution order (MRO) for "{defn.name}"', defn, ) self.set_dummy_mro(defn.info) @@ -3437,10 +3569,10 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool: def analyze_lvalues(self, s: AssignmentStmt) -> None: # We cannot use s.type, because analyze_simple_literal_type() will set it. explicit = s.unanalyzed_type is not None - final_type = self.unwrap_final_type(s.unanalyzed_type) - if final_type is not None: + if self.is_final_type(s.unanalyzed_type): # We need to exclude bare Final. - if not final_type.args: + assert isinstance(s.unanalyzed_type, UnboundType) + if not s.unanalyzed_type.args: explicit = False if s.rvalue: @@ -3506,19 +3638,19 @@ def unwrap_final(self, s: AssignmentStmt) -> bool: Returns True if Final[...] was present. """ - final_type = self.unwrap_final_type(s.unanalyzed_type) - if final_type is None: + if not s.unanalyzed_type or not self.is_final_type(s.unanalyzed_type): return False - if len(final_type.args) > 1: - self.fail("Final[...] takes at most one type argument", final_type) + assert isinstance(s.unanalyzed_type, UnboundType) + if len(s.unanalyzed_type.args) > 1: + self.fail("Final[...] takes at most one type argument", s.unanalyzed_type) invalid_bare_final = False - if not final_type.args: + if not s.unanalyzed_type.args: s.type = None if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: invalid_bare_final = True self.fail("Type in Final[...] can only be omitted if there is an initializer", s) else: - s.type = final_type.args[0] + s.type = s.unanalyzed_type.args[0] if s.type is not None and self.is_classvar(s.type): self.fail("Variable should not be annotated with both ClassVar and Final", s) @@ -3744,7 +3876,9 @@ def analyze_alias( dynamic = bool(self.function_stack and self.function_stack[-1].is_dynamic()) global_scope = not self.type and not self.function_stack try: - typ = expr_to_unanalyzed_type(rvalue, self.options, self.is_stub_file) + typ = expr_to_unanalyzed_type( + rvalue, self.options, self.is_stub_file, lookup_qualified=self.lookup_qualified + ) except TypeTranslationError: self.fail( "Invalid type alias: expression is not a valid type", rvalue, code=codes.VALID_TYPE @@ -3755,21 +3889,8 @@ def analyze_alias( tvar_defs: list[TypeVarLikeType] = [] namespace = self.qualified_name(name) alias_type_vars = found_type_vars if declared_type_vars is None else declared_type_vars - last_tvar_name_with_default: str | None = None with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): - for name, tvar_expr in alias_type_vars: - tvar_expr.default = tvar_expr.default.accept( - TypeVarDefaultTranslator(self, tvar_expr.name, typ) - ) - tvar_def = self.tvar_scope.bind_new(name, tvar_expr) - if last_tvar_name_with_default is not None and not tvar_def.has_default(): - self.msg.tvar_without_default_type( - tvar_def.name, last_tvar_name_with_default, typ - ) - tvar_def.default = AnyType(TypeOfAny.from_error) - elif tvar_def.has_default(): - last_tvar_name_with_default = tvar_def.name - tvar_defs.append(tvar_def) + tvar_defs = self.tvar_defs_from_tvars(alias_type_vars, typ) if python_3_12_type_alias: with self.allow_unbound_tvars_set(): @@ -3781,6 +3902,7 @@ def analyze_alias( self.tvar_scope, self.plugin, self.options, + self.cur_mod_node, self.is_typeshed_stub_file, allow_placeholder=allow_placeholder, in_dynamic_func=dynamic, @@ -3935,6 +4057,11 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: # When this type alias gets "inlined", the Any is not explicit anymore, # so we need to replace it with non-explicit Anys. res = make_any_non_explicit(res) + if self.options.disallow_any_unimported and has_any_from_unimported_type(res): + # Only show error message once, when the type is fully analyzed. + if not has_placeholder(res): + self.msg.unimported_type_becomes_any("Type alias target", res, s) + res = make_any_non_unimported(res) # Note: with the new (lazy) type alias representation we only need to set no_args to True # if the expected number of arguments is non-zero, so that aliases like `A = List` work # but not aliases like `A = TypeAliasType("A", List)` as these need explicit type params. @@ -3988,6 +4115,8 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: existing.node.alias_tvars = alias_tvars existing.node.no_args = no_args updated = True + # Invalidate recursive status cache in case it was previously set. + existing.node._is_recursive = None else: # Otherwise just replace existing placeholder with type alias. existing.node = alias_node @@ -4128,6 +4257,7 @@ def analyze_lvalue( is_final: bool = False, escape_comprehensions: bool = False, has_explicit_value: bool = False, + is_index_var: bool = False, ) -> None: """Analyze an lvalue or assignment target. @@ -4138,6 +4268,7 @@ def analyze_lvalue( escape_comprehensions: If we are inside a comprehension, set the variable in the enclosing scope instead. This implements https://www.python.org/dev/peps/pep-0572/#scope-of-the-target + is_index_var: If lval is the index variable in a for loop """ if escape_comprehensions: assert isinstance(lval, NameExpr), "assignment expression target must be NameExpr" @@ -4148,6 +4279,7 @@ def analyze_lvalue( is_final, escape_comprehensions, has_explicit_value=has_explicit_value, + is_index_var=is_index_var, ) elif isinstance(lval, MemberExpr): self.analyze_member_lvalue(lval, explicit_type, is_final, has_explicit_value) @@ -4174,6 +4306,7 @@ def analyze_name_lvalue( is_final: bool, escape_comprehensions: bool, has_explicit_value: bool, + is_index_var: bool, ) -> None: """Analyze an lvalue that targets a name expression. @@ -4210,9 +4343,18 @@ def analyze_name_lvalue( lvalue, ) + if explicit_type and has_explicit_value: + self.fail("Enum members must be left unannotated", lvalue) + self.note( + "See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members", + lvalue, + ) + if (not existing or isinstance(existing.node, PlaceholderNode)) and not outer: # Define new variable. - var = self.make_name_lvalue_var(lvalue, kind, not explicit_type, has_explicit_value) + var = self.make_name_lvalue_var( + lvalue, kind, not explicit_type, has_explicit_value, is_index_var + ) added = self.add_symbol(name, var, lvalue, escape_comprehensions=escape_comprehensions) # Only bind expression if we successfully added name to symbol table. if added: @@ -4264,7 +4406,12 @@ def is_alias_for_final_name(self, name: str) -> bool: return existing is not None and is_final_node(existing.node) def make_name_lvalue_var( - self, lvalue: NameExpr, kind: int, inferred: bool, has_explicit_value: bool + self, + lvalue: NameExpr, + kind: int, + inferred: bool, + has_explicit_value: bool, + is_index_var: bool, ) -> Var: """Return a Var node for an lvalue that is a name expression.""" name = lvalue.name @@ -4283,6 +4430,7 @@ def make_name_lvalue_var( v._fullname = name v.is_ready = False # Type not inferred yet v.has_explicit_value = has_explicit_value + v.is_index_var = is_index_var return v def make_name_lvalue_point_to_existing_def( @@ -4530,6 +4678,40 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: self.add_symbol(name, call.analyzed, s) return True + def check_typevar_default(self, default: Type, context: Context) -> Type: + typ = get_proper_type(default) + if isinstance(typ, AnyType) and typ.is_from_error: + self.fail( + message_registry.TYPEVAR_ARG_MUST_BE_TYPE.format("TypeVar", "default"), context + ) + return default + + def check_paramspec_default(self, default: Type, context: Context) -> Type: + typ = get_proper_type(default) + if isinstance(typ, Parameters): + for i, arg_type in enumerate(typ.arg_types): + arg_ptype = get_proper_type(arg_type) + if isinstance(arg_ptype, AnyType) and arg_ptype.is_from_error: + self.fail(f"Argument {i} of ParamSpec default must be a type", context) + elif ( + isinstance(typ, AnyType) + and typ.is_from_error + or not isinstance(typ, (AnyType, UnboundType)) + ): + self.fail( + "The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec", + context, + ) + default = AnyType(TypeOfAny.from_error) + return default + + def check_typevartuple_default(self, default: Type, context: Context) -> Type: + typ = get_proper_type(default) + if not isinstance(typ, UnpackType): + self.fail("The default argument to TypeVarTuple must be an Unpacked tuple", context) + default = AnyType(TypeOfAny.from_error) + return default + def check_typevarlike_name(self, call: CallExpr, name: str, context: Context) -> bool: """Checks that the name of a TypeVar or ParamSpec matches its variable.""" name = unmangle(name) @@ -4737,23 +4919,7 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: report_invalid_typevar_arg=False, ) default = tv_arg or AnyType(TypeOfAny.from_error) - if isinstance(tv_arg, Parameters): - for i, arg_type in enumerate(tv_arg.arg_types): - typ = get_proper_type(arg_type) - if isinstance(typ, AnyType) and typ.is_from_error: - self.fail( - f"Argument {i} of ParamSpec default must be a type", param_value - ) - elif ( - isinstance(default, AnyType) - and default.is_from_error - or not isinstance(default, (AnyType, UnboundType)) - ): - self.fail( - "The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec", - param_value, - ) - default = AnyType(TypeOfAny.from_error) + default = self.check_paramspec_default(default, param_value) else: # ParamSpec is different from a regular TypeVar: # arguments are not semantically valid. But, allowed in runtime. @@ -4814,12 +4980,7 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: allow_unpack=True, ) default = tv_arg or AnyType(TypeOfAny.from_error) - if not isinstance(default, UnpackType): - self.fail( - "The default argument to TypeVarTuple must be an Unpacked tuple", - param_value, - ) - default = AnyType(TypeOfAny.from_error) + default = self.check_typevartuple_default(default, param_value) else: self.fail(f'Unexpected keyword argument "{param_name}" for "TypeVarTuple"', s) @@ -4888,7 +5049,11 @@ def analyze_value_types(self, items: list[Expression]) -> list[Type]: # soon, even if some value is not ready yet, see process_typevar_parameters() # for an example. analyzed = PlaceholderType(None, [], node.line) - result.append(analyzed) + if has_type_vars(analyzed): + self.fail(message_registry.TYPE_VAR_GENERIC_CONSTRAINT_TYPE, node) + result.append(AnyType(TypeOfAny.from_error)) + else: + result.append(analyzed) except TypeTranslationError: self.fail("Type expected", node) result.append(AnyType(TypeOfAny.from_error)) @@ -4934,18 +5099,13 @@ def is_classvar(self, typ: Type) -> bool: return False return sym.node.fullname == "typing.ClassVar" - def unwrap_final_type(self, typ: Type | None) -> UnboundType | None: - if typ is None: - return None - typ = typ.resolve_string_annotation() + def is_final_type(self, typ: Type | None) -> bool: if not isinstance(typ, UnboundType): - return None + return False sym = self.lookup_qualified(typ.name, typ) if not sym or not sym.node: - return None - if sym.node.fullname in FINAL_TYPE_NAMES: - return typ - return None + return False + return sym.node.fullname in FINAL_TYPE_NAMES def fail_invalid_classvar(self, context: Context) -> None: self.fail(message_registry.CLASS_VAR_OUTSIDE_OF_CLASS, context) @@ -5139,6 +5299,8 @@ def visit_return_stmt(self, s: ReturnStmt) -> None: self.statement = s if not self.is_func_scope(): self.fail('"return" outside function', s) + if self.return_stmt_inside_except_star_block: + self.fail('"return" not allowed in except* block', s, serious=True) if s.expr: s.expr.accept(self) @@ -5172,7 +5334,8 @@ def visit_while_stmt(self, s: WhileStmt) -> None: self.statement = s s.expr.accept(self) self.loop_depth[-1] += 1 - s.body.accept(self) + with self.inside_except_star_block_set(value=False, entering_loop=True): + s.body.accept(self) self.loop_depth[-1] -= 1 self.visit_block_maybe(s.else_body) @@ -5185,7 +5348,7 @@ def visit_for_stmt(self, s: ForStmt) -> None: s.expr.accept(self) # Bind index variables and check if they define new names. - self.analyze_lvalue(s.index, explicit_type=s.index_type is not None) + self.analyze_lvalue(s.index, explicit_type=s.index_type is not None, is_index_var=True) if s.index_type: if self.is_classvar(s.index_type): self.fail_invalid_classvar(s.index) @@ -5196,20 +5359,24 @@ def visit_for_stmt(self, s: ForStmt) -> None: s.index_type = analyzed self.loop_depth[-1] += 1 - self.visit_block(s.body) + with self.inside_except_star_block_set(value=False, entering_loop=True): + self.visit_block(s.body) self.loop_depth[-1] -= 1 - self.visit_block_maybe(s.else_body) def visit_break_stmt(self, s: BreakStmt) -> None: self.statement = s if self.loop_depth[-1] == 0: self.fail('"break" outside loop', s, serious=True, blocker=True) + if self.inside_except_star_block: + self.fail('"break" not allowed in except* block', s, serious=True) def visit_continue_stmt(self, s: ContinueStmt) -> None: self.statement = s if self.loop_depth[-1] == 0: self.fail('"continue" outside loop', s, serious=True, blocker=True) + if self.inside_except_star_block: + self.fail('"continue" not allowed in except* block', s, serious=True) def visit_if_stmt(self, s: IfStmt) -> None: self.statement = s @@ -5230,7 +5397,8 @@ def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor[None]) -> None: type.accept(visitor) if var: self.analyze_lvalue(var) - handler.accept(visitor) + with self.inside_except_star_block_set(self.inside_except_star_block or s.is_star): + handler.accept(visitor) if s.else_body: s.else_body.accept(visitor) if s.finally_body: @@ -5407,6 +5575,9 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: # When this type alias gets "inlined", the Any is not explicit anymore, # so we need to replace it with non-explicit Anys. res = make_any_non_explicit(res) + if self.options.disallow_any_unimported and has_any_from_unimported_type(res): + self.msg.unimported_type_becomes_any("Type alias target", res, s) + res = make_any_non_unimported(res) eager = self.is_func_scope() if isinstance(res, ProperType) and isinstance(res, Instance) and not res.args: fix_instance(res, self.fail, self.note, disallow_any=False, options=self.options) @@ -5420,6 +5591,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None: eager=eager, python_3_12_type_alias=True, ) + s.alias_node = alias_node if ( existing @@ -5903,9 +6075,8 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: if has_param_spec and num_args == 1 and types: first_arg = get_proper_type(types[0]) - if not ( - len(types) == 1 and isinstance(first_arg, (Parameters, ParamSpecType, AnyType)) - ): + single_any = len(types) == 1 and isinstance(first_arg, AnyType) + if not (single_any or any(isinstance(t, (Parameters, ParamSpecType)) for t in types)): types = [Parameters(types, [ARG_POS] * len(types), [None] * len(types))] return types @@ -6383,18 +6554,46 @@ def lookup_fully_qualified_or_none(self, fullname: str) -> SymbolTableNode | Non Note that this can't be used for names nested in class namespaces. """ # TODO: unify/clean-up/simplify lookup methods, see #4157. - # TODO: support nested classes (but consider performance impact, - # we might keep the module level only lookup for thing like 'builtins.int'). - assert "." in fullname module, name = fullname.rsplit(".", maxsplit=1) - if module not in self.modules: - return None - filenode = self.modules[module] - result = filenode.names.get(name) - if result is None and self.is_incomplete_namespace(module): - # TODO: More explicit handling of incomplete refs? - self.record_incomplete_ref() - return result + + if module in self.modules: + # If the module exists, look up the name in the module. + # This is the common case. + filenode = self.modules[module] + result = filenode.names.get(name) + if result is None and self.is_incomplete_namespace(module): + # TODO: More explicit handling of incomplete refs? + self.record_incomplete_ref() + return result + else: + # Else, try to find the longest prefix of the module name that is in the modules dictionary. + splitted_modules = fullname.split(".") + names = [] + + while splitted_modules and ".".join(splitted_modules) not in self.modules: + names.append(splitted_modules.pop()) + + if not splitted_modules or not names: + # If no module or name is found, return None. + return None + + # Reverse the names list to get the correct order of names. + names.reverse() + + module = ".".join(splitted_modules) + filenode = self.modules[module] + result = filenode.names.get(names[0]) + + if result is None and self.is_incomplete_namespace(module): + # TODO: More explicit handling of incomplete refs? + self.record_incomplete_ref() + + for part in names[1:]: + if result is not None and isinstance(result.node, TypeInfo): + result = result.node.names.get(part) + else: + return None + return result def object_type(self) -> Instance: return self.named_type("builtins.object") @@ -6947,6 +7146,7 @@ def name_not_defined(self, name: str, ctx: Context, namespace: str | None = None namespace is None and self.type and not self.is_func_scope() + and self.incomplete_type_stack and self.incomplete_type_stack[-1] and not self.final_iteration ): @@ -7146,7 +7346,7 @@ def type_analyzer( allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, allow_placeholder: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -7160,12 +7360,13 @@ def type_analyzer( tvar_scope, self.plugin, self.options, + self.cur_mod_node, self.is_typeshed_stub_file, allow_unbound_tvars=allow_unbound_tvars, allow_tuple_literal=allow_tuple_literal, report_invalid_types=report_invalid_types, allow_placeholder=allow_placeholder, - allow_required=allow_required, + allow_typed_dict_special_forms=allow_typed_dict_special_forms, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, prohibit_self_type=prohibit_self_type, @@ -7188,7 +7389,7 @@ def anal_type( allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, allow_placeholder: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -7223,7 +7424,7 @@ def anal_type( allow_unbound_tvars=allow_unbound_tvars, allow_tuple_literal=allow_tuple_literal, allow_placeholder=allow_placeholder, - allow_required=allow_required, + allow_typed_dict_special_forms=allow_typed_dict_special_forms, allow_param_spec_literals=allow_param_spec_literals, allow_unpack=allow_unpack, report_invalid_types=report_invalid_types, @@ -7433,6 +7634,21 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: return t.copy_modified(args=[a.accept(self) for a in t.args]) +def make_any_non_unimported(t: Type) -> Type: + """Replace all Any types that come from unimported types with special form Any.""" + return t.accept(MakeAnyNonUnimported()) + + +class MakeAnyNonUnimported(TrivialSyntheticTypeTranslator): + def visit_any(self, t: AnyType) -> Type: + if t.type_of_any == TypeOfAny.from_unimported_type: + return t.copy_modified(TypeOfAny.special_form, missing_import_name=None) + return t + + def visit_type_alias_type(self, t: TypeAliasType) -> Type: + return t.copy_modified(args=[a.accept(self) for a in t.args]) + + def apply_semantic_analyzer_patches(patches: list[tuple[int, Callable[[], None]]]) -> None: """Call patch callbacks in the right order. diff --git a/mypy/semanal_classprop.py b/mypy/semanal_classprop.py index b5f1b21817612..c5ad34122f6c8 100644 --- a/mypy/semanal_classprop.py +++ b/mypy/semanal_classprop.py @@ -122,11 +122,12 @@ def check_protocol_status(info: TypeInfo, errors: Errors) -> None: if info.is_protocol: for type in info.bases: if not type.type.is_protocol and type.type.fullname != "builtins.object": - - def report(message: str, severity: str) -> None: - errors.report(info.line, info.column, message, severity=severity) - - report("All bases of a protocol must be protocols", "error") + errors.report( + info.line, + info.column, + "All bases of a protocol must be protocols", + severity="error", + ) def calculate_class_vars(info: TypeInfo) -> None: diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 30e0bd56c3120..b1e267b4c781f 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -10,6 +10,7 @@ from mypy.nodes import ( ARG_NAMED, ARG_POS, + EXCLUDED_ENUM_ATTRIBUTES, MDEF, AssignmentStmt, CallExpr, @@ -30,7 +31,7 @@ ) from mypy.options import Options from mypy.semanal_shared import SemanticAnalyzerInterface -from mypy.types import ENUM_REMOVED_PROPS, LiteralType, get_proper_type +from mypy.types import LiteralType, get_proper_type # Note: 'enum.EnumMeta' is deliberately excluded from this list. Classes that directly use # enum.EnumMeta do not necessarily automatically have the 'name' and 'value' attributes. @@ -43,7 +44,7 @@ "value", "_name_", "_value_", - *ENUM_REMOVED_PROPS, + *EXCLUDED_ENUM_ATTRIBUTES, # Also attributes from `object`: "__module__", "__annotations__", @@ -143,6 +144,12 @@ def build_enum_call_typeinfo( var = Var(item) var.info = info var.is_property = True + # When an enum is created by its functional form `Enum(name, values)` + # - if it is a string it is first split by commas/whitespace + # - if it is an iterable of single items each item is assigned a value starting at `start` + # - if it is an iterable of (name, value) then the given values will be used + # either way, each item should be treated as if it has an explicit value. + var.has_explicit_value = True var._fullname = f"{info.fullname}.{item}" info.names[item] = SymbolTableNode(MDEF, var) return info diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 1185a38215530..09a1223be6aaa 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -291,6 +291,8 @@ def process_top_level_function( deferred, incomplete, progress = semantic_analyze_target( target, module, state, node, active_type, final_iteration, patches ) + if not incomplete: + state.manager.incomplete_namespaces.discard(module) if final_iteration: assert not deferred, "Must not defer during final iteration" if not progress: diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 768dd265b3383..7c6da7721e8fd 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -380,8 +380,7 @@ def parse_namedtuple_args( rename = arg.name == "True" else: self.fail( - 'Boolean literal expected as the "rename" argument to ' - f"{type_name}()", + f'Boolean literal expected as the "rename" argument to {type_name}()', arg, code=ARG_TYPE, ) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index db19f074911f5..cb0bdebab724f 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -181,7 +181,7 @@ def anal_type( tvar_scope: TypeVarLikeScope | None = None, allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_placeholder: bool = False, report_invalid_types: bool = True, prohibit_self_type: str | None = None, diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index dbf5136afa1b7..646bb28a3b6e5 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -39,6 +39,7 @@ get_proper_types, split_with_prefix_and_suffix, ) +from mypy.typevartuples import erased_vars class TypeArgumentAnalyzer(MixedTraverserVisitor): @@ -89,7 +90,14 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: return self.seen_aliases.add(t) assert t.alias is not None, f"Unfixed type alias {t.type_ref}" - is_error = self.validate_args(t.alias.name, tuple(t.args), t.alias.alias_tvars, t) + is_error, is_invalid = self.validate_args( + t.alias.name, tuple(t.args), t.alias.alias_tvars, t + ) + if is_invalid: + # If there is an arity error (e.g. non-Parameters used for ParamSpec etc.), + # then it is safer to erase the arguments completely, to avoid crashes later. + # TODO: can we move this logic to typeanal.py? + t.args = erased_vars(t.alias.alias_tvars, TypeOfAny.from_error) if not is_error: # If there was already an error for the alias itself, there is no point in checking # the expansion, most likely it will result in the same kind of error. @@ -113,7 +121,9 @@ def visit_instance(self, t: Instance) -> None: info = t.type if isinstance(info, FakeInfo): return # https://github.com/python/mypy/issues/11079 - self.validate_args(info.name, t.args, info.defn.type_vars, t) + _, is_invalid = self.validate_args(info.name, t.args, info.defn.type_vars, t) + if is_invalid: + t.args = tuple(erased_vars(info.defn.type_vars, TypeOfAny.from_error)) if t.type.fullname == "builtins.tuple" and len(t.args) == 1: # Normalize Tuple[*Tuple[X, ...], ...] -> Tuple[X, ...] arg = t.args[0] @@ -125,7 +135,7 @@ def visit_instance(self, t: Instance) -> None: def validate_args( self, name: str, args: tuple[Type, ...], type_vars: list[TypeVarLikeType], ctx: Context - ) -> bool: + ) -> tuple[bool, bool]: if any(isinstance(v, TypeVarTupleType) for v in type_vars): prefix = next(i for (i, v) in enumerate(type_vars) if isinstance(v, TypeVarTupleType)) tvt = type_vars[prefix] @@ -136,10 +146,11 @@ def validate_args( args = start + (TupleType(list(middle), tvt.tuple_fallback),) + end is_error = False + is_invalid = False for (i, arg), tvar in zip(enumerate(args), type_vars): if isinstance(tvar, TypeVarType): if isinstance(arg, ParamSpecType): - is_error = True + is_invalid = True self.fail( INVALID_PARAM_SPEC_LOCATION.format(format_type(arg, self.options)), ctx, @@ -152,7 +163,7 @@ def validate_args( ) continue if isinstance(arg, Parameters): - is_error = True + is_invalid = True self.fail( f"Cannot use {format_type(arg, self.options)} for regular type variable," " only for ParamSpec", @@ -205,13 +216,16 @@ def validate_args( if not isinstance( get_proper_type(arg), (ParamSpecType, Parameters, AnyType, UnboundType) ): + is_invalid = True self.fail( "Can only replace ParamSpec with a parameter types list or" f" another ParamSpec, got {format_type(arg, self.options)}", ctx, code=codes.VALID_TYPE, ) - return is_error + if is_invalid: + is_error = True + return is_error, is_invalid def visit_unpack_type(self, typ: UnpackType) -> None: super().visit_unpack_type(typ) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index eee98d4d20fa7..d081898bf010c 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -8,6 +8,7 @@ from mypy.errorcodes import ErrorCode from mypy.expandtype import expand_type from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type +from mypy.message_registry import TYPEDDICT_OVERRIDE_MERGE from mypy.messages import MessageBuilder from mypy.nodes import ( ARG_NAMED, @@ -42,6 +43,7 @@ from mypy.types import ( TPDICT_NAMES, AnyType, + ReadOnlyType, RequiredType, Type, TypedDictType, @@ -101,13 +103,15 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N and defn.base_type_exprs[0].fullname in TPDICT_NAMES ): # Building a new TypedDict - fields, types, statements, required_keys = self.analyze_typeddict_classdef_fields(defn) + fields, types, statements, required_keys, readonly_keys = ( + self.analyze_typeddict_classdef_fields(defn) + ) if fields is None: return True, None # Defer if self.api.is_func_scope() and "@" not in defn.name: defn.name += "@" + str(defn.line) info = self.build_typeddict_typeinfo( - defn.name, fields, types, required_keys, defn.line, existing_info + defn.name, fields, types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -153,10 +157,13 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N keys: list[str] = [] types = [] required_keys = set() + readonly_keys = set() # Iterate over bases in reverse order so that leftmost base class' keys take precedence for base in reversed(typeddict_bases): - self.add_keys_and_types_from_base(base, keys, types, required_keys, defn) - (new_keys, new_types, new_statements, new_required_keys) = ( + self.add_keys_and_types_from_base( + base, keys, types, required_keys, readonly_keys, defn + ) + (new_keys, new_types, new_statements, new_required_keys, new_readonly_keys) = ( self.analyze_typeddict_classdef_fields(defn, keys) ) if new_keys is None: @@ -164,8 +171,9 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N keys.extend(new_keys) types.extend(new_types) required_keys.update(new_required_keys) + readonly_keys.update(new_readonly_keys) info = self.build_typeddict_typeinfo( - defn.name, keys, types, required_keys, defn.line, existing_info + defn.name, keys, types, required_keys, readonly_keys, defn.line, existing_info ) defn.analyzed = TypedDictExpr(info) defn.analyzed.line = defn.line @@ -179,6 +187,7 @@ def add_keys_and_types_from_base( keys: list[str], types: list[Type], required_keys: set[str], + readonly_keys: set[str], ctx: Context, ) -> None: base_args: list[Type] = [] @@ -216,10 +225,11 @@ def add_keys_and_types_from_base( valid_items = self.map_items_to_base(valid_items, tvars, base_args) for key in base_items: if key in keys: - self.fail(f'Overwriting TypedDict field "{key}" while merging', ctx) + self.fail(TYPEDDICT_OVERRIDE_MERGE.format(key), ctx) keys.extend(valid_items.keys()) types.extend(valid_items.values()) required_keys.update(base_typed_dict.required_keys) + readonly_keys.update(base_typed_dict.readonly_keys) def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None: """Analyze arguments of base type expressions as types. @@ -240,7 +250,9 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None: self.fail("Invalid TypedDict type argument", ctx) return None analyzed = self.api.anal_type( - type, allow_required=True, allow_placeholder=not self.api.is_func_scope() + type, + allow_typed_dict_special_forms=True, + allow_placeholder=not self.api.is_func_scope(), ) if analyzed is None: return None @@ -269,7 +281,7 @@ def map_items_to_base( def analyze_typeddict_classdef_fields( self, defn: ClassDef, oldfields: list[str] | None = None - ) -> tuple[list[str] | None, list[Type], list[Statement], set[str]]: + ) -> tuple[list[str] | None, list[Type], list[Statement], set[str], set[str]]: """Analyze fields defined in a TypedDict class definition. This doesn't consider inherited fields (if any). Also consider totality, @@ -310,18 +322,20 @@ def analyze_typeddict_classdef_fields( # Append stmt, name, and type in this case... fields.append(name) statements.append(stmt) - if stmt.type is None: + if stmt.unanalyzed_type is None: types.append(AnyType(TypeOfAny.unannotated)) else: analyzed = self.api.anal_type( - stmt.type, - allow_required=True, + stmt.unanalyzed_type, + allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", ) if analyzed is None: - return None, [], [], set() # Need to defer + return None, [], [], set(), set() # Need to defer types.append(analyzed) + if not has_placeholder(analyzed): + stmt.type = self.extract_meta_info(analyzed, stmt)[0] # ...despite possible minor failures that allow further analysis. if stmt.type is None or hasattr(stmt, "new_syntax") and not stmt.new_syntax: self.fail(TPDICT_CLASS_ERROR, stmt) @@ -337,17 +351,49 @@ def analyze_typeddict_classdef_fields( if key == "total": continue self.msg.unexpected_keyword_argument_for_function(for_function, key, defn) - required_keys = { - field - for (field, t) in zip(fields, types) - if (total or (isinstance(t, RequiredType) and t.required)) - and not (isinstance(t, RequiredType) and not t.required) - } - types = [ # unwrap Required[T] to just T - t.item if isinstance(t, RequiredType) else t for t in types - ] - - return fields, types, statements, required_keys + + res_types = [] + readonly_keys = set() + required_keys = set() + for field, t in zip(fields, types): + typ, required, readonly = self.extract_meta_info(t) + res_types.append(typ) + if (total or required is True) and required is not False: + required_keys.add(field) + if readonly: + readonly_keys.add(field) + + return fields, res_types, statements, required_keys, readonly_keys + + def extract_meta_info( + self, typ: Type, context: Context | None = None + ) -> tuple[Type, bool | None, bool]: + """Unwrap all metadata types.""" + is_required = None # default, no modification + readonly = False # by default all is mutable + + seen_required = False + seen_readonly = False + while isinstance(typ, (RequiredType, ReadOnlyType)): + if isinstance(typ, RequiredType): + if context is not None and seen_required: + self.fail( + '"{}" type cannot be nested'.format( + "Required[]" if typ.required else "NotRequired[]" + ), + context, + code=codes.VALID_TYPE, + ) + is_required = typ.required + seen_required = True + typ = typ.item + if isinstance(typ, ReadOnlyType): + if context is not None and seen_readonly: + self.fail('"ReadOnly[]" type cannot be nested', context, code=codes.VALID_TYPE) + readonly = True + seen_readonly = True + typ = typ.item + return typ, is_required, readonly def check_typeddict( self, node: Expression, var_name: str | None, is_func_scope: bool @@ -386,7 +432,7 @@ def check_typeddict( name += "@" + str(call.line) else: name = var_name = "TypedDict@" + str(call.line) - info = self.build_typeddict_typeinfo(name, [], [], set(), call.line, None) + info = self.build_typeddict_typeinfo(name, [], [], set(), set(), call.line, None) else: if var_name is not None and name != var_name: self.fail( @@ -405,8 +451,11 @@ def check_typeddict( if (total or (isinstance(t, RequiredType) and t.required)) and not (isinstance(t, RequiredType) and not t.required) } - types = [ # unwrap Required[T] to just T - t.item if isinstance(t, RequiredType) else t for t in types + readonly_keys = { + field for (field, t) in zip(items, types) if isinstance(t, ReadOnlyType) + } + types = [ # unwrap Required[T] or ReadOnly[T] to just T + t.item if isinstance(t, (RequiredType, ReadOnlyType)) else t for t in types ] # Perform various validations after unwrapping. @@ -423,7 +472,7 @@ def check_typeddict( if isinstance(node.analyzed, TypedDictExpr): existing_info = node.analyzed.info info = self.build_typeddict_typeinfo( - name, items, types, required_keys, call.line, existing_info + name, items, types, required_keys, readonly_keys, call.line, existing_info ) info.line = node.line # Store generated TypeInfo under both names, see semanal_namedtuple for more details. @@ -505,21 +554,11 @@ def parse_typeddict_fields_with_types( field_type_expr, self.options, self.api.is_stub_file ) except TypeTranslationError: - if ( - isinstance(field_type_expr, CallExpr) - and isinstance(field_type_expr.callee, RefExpr) - and field_type_expr.callee.fullname in TPDICT_NAMES - ): - self.fail_typeddict_arg( - "Inline TypedDict types not supported; use assignment to define TypedDict", - field_type_expr, - ) - else: - self.fail_typeddict_arg("Invalid field type", field_type_expr) + self.fail_typeddict_arg("Use dict literal for nested TypedDict", field_type_expr) return [], [], False analyzed = self.api.anal_type( type, - allow_required=True, + allow_typed_dict_special_forms=True, allow_placeholder=not self.api.is_func_scope(), prohibit_self_type="TypedDict item type", ) @@ -540,6 +579,7 @@ def build_typeddict_typeinfo( items: list[str], types: list[Type], required_keys: set[str], + readonly_keys: set[str], line: int, existing_info: TypeInfo | None, ) -> TypeInfo: @@ -551,7 +591,9 @@ def build_typeddict_typeinfo( ) assert fallback is not None info = existing_info or self.api.basic_new_typeinfo(name, fallback, line) - typeddict_type = TypedDictType(dict(zip(items, types)), required_keys, fallback) + typeddict_type = TypedDictType( + dict(zip(items, types)), required_keys, readonly_keys, fallback + ) if info.special_alias and has_placeholder(info.special_alias.target): self.api.process_placeholder( None, "TypedDict item", info, force_progress=typeddict_type != info.typeddict_type diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index f8a874005adba..85f77a269e43e 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -219,7 +219,9 @@ def snapshot_symbol_table(name_prefix: str, table: SymbolTable) -> dict[str, Sym assert symbol.kind != UNBOUND_IMPORTED if node and get_prefix(node.fullname) != name_prefix: # This is a cross-reference to a node defined in another module. - result[name] = ("CrossRef", common) + # Include the node kind (FuncDef, Decorator, TypeInfo, ...), so that we will + # reprocess when a *new* node is created instead of merging an existing one. + result[name] = ("CrossRef", common, type(node).__name__) else: result[name] = snapshot_definition(node, common) return result @@ -254,6 +256,7 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb signature, is_trivial_body, dataclass_transform_spec.serialize() if dataclass_transform_spec is not None else None, + node.deprecated if isinstance(node, FuncDef) else None, ) elif isinstance(node, Var): return ("Var", common, snapshot_optional_type(node.type), node.is_final) @@ -300,6 +303,7 @@ def snapshot_definition(node: SymbolNode | None, common: SymbolSnapshot) -> Symb [snapshot_type(base) for base in node.bases], [snapshot_type(p) for p in node._promote], dataclass_transform_spec.serialize() if dataclass_transform_spec is not None else None, + node.deprecated, ) prefix = node.fullname symbol_table = snapshot_symbol_table(prefix, node.names) @@ -475,7 +479,8 @@ def visit_tuple_type(self, typ: TupleType) -> SnapshotItem: def visit_typeddict_type(self, typ: TypedDictType) -> SnapshotItem: items = tuple((key, snapshot_type(item_type)) for key, item_type in typ.items.items()) required = tuple(sorted(typ.required_keys)) - return ("TypedDictType", items, required) + readonly = tuple(sorted(typ.readonly_keys)) + return ("TypedDictType", items, required, readonly) def visit_literal_type(self, typ: LiteralType) -> SnapshotItem: return ("LiteralType", snapshot_type(typ.fallback), typ.value) diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index e6648fbb4be72..5dc2544223287 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -160,7 +160,7 @@ def replacement_map_from_symbol_table( ): new_node = new[name] if ( - type(new_node.node) == type(node.node) # noqa: E721 + type(new_node.node) == type(node.node) and new_node.node and node.node and new_node.node.fullname == node.node.fullname @@ -507,8 +507,7 @@ def visit_typeddict_type(self, typ: TypedDictType) -> None: typ.fallback.accept(self) def visit_raw_expression_type(self, t: RawExpressionType) -> None: - if t.node is not None: - t.node.accept(self) + pass def visit_literal_type(self, typ: LiteralType) -> None: typ.fallback.accept(self) diff --git a/mypy/server/deps.py b/mypy/server/deps.py index 9ed2d4549629c..6376600ffc0c4 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -56,7 +56,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a * 'mod.Cls' represents each method in class 'mod.Cls' + the top-level of the module 'mod'. (To simplify the implementation, there is no location that only includes the body of a class without the entire surrounding module top level.) -* Trigger '<...>' as a location is an indirect way of referring to to all +* Trigger '<...>' as a location is an indirect way of referring to all locations triggered by the trigger. These indirect locations keep the dependency map smaller and easier to manage. diff --git a/mypy/server/update.py b/mypy/server/update.py index 0cc7a2229514d..fdc311bbfa6b7 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -146,11 +146,7 @@ TypeInfo, ) from mypy.options import Options -from mypy.semanal_main import ( - core_modules, - semantic_analysis_for_scc, - semantic_analysis_for_targets, -) +from mypy.semanal_main import semantic_analysis_for_scc, semantic_analysis_for_targets from mypy.server.astdiff import ( SymbolSnapshot, compare_symbol_table_snapshots, @@ -162,11 +158,12 @@ from mypy.server.target import trigger_to_target from mypy.server.trigger import WILDCARD_TAG, make_trigger from mypy.typestate import type_state -from mypy.util import module_prefix, split_target +from mypy.util import is_stdlib_file, module_prefix, split_target MAX_ITER: Final = 1000 -SENSITIVE_INTERNAL_MODULES = tuple(core_modules) + ("mypy_extensions", "typing_extensions") +# These are modules beyond stdlib that have some special meaning for mypy. +SENSITIVE_INTERNAL_MODULES = ("mypy_extensions", "typing_extensions") class FineGrainedBuildManager: @@ -406,7 +403,10 @@ def update_module( # builtins and friends could potentially get triggered because # of protocol stuff, but nothing good could possibly come from # actually updating them. - if module in SENSITIVE_INTERNAL_MODULES: + if ( + is_stdlib_file(self.manager.options.abs_custom_typeshed_dir, path) + or module in SENSITIVE_INTERNAL_MODULES + ): return [], (module, path), None manager = self.manager @@ -1059,8 +1059,7 @@ def find_symbol_tables_recursive(prefix: str, symbols: SymbolTable) -> dict[str, Returns a dictionary from full name to corresponding symbol table. """ - result = {} - result[prefix] = symbols + result = {prefix: symbols} for name, node in symbols.items(): if isinstance(node.node, TypeInfo) and node.node.fullname.startswith(prefix + "."): more = find_symbol_tables_recursive(prefix + "." + name, node.node.names) diff --git a/mypy/solve.py b/mypy/solve.py index bb87b6576ada2..8a1495a9a246b 100644 --- a/mypy/solve.py +++ b/mypy/solve.py @@ -553,6 +553,11 @@ def pre_validate_solutions( """ new_solutions: list[Type | None] = [] for t, s in zip(original_vars, solutions): + if is_callable_protocol(t.upper_bound): + # This is really ad-hoc, but a proper fix would be much more complex, + # and otherwise this may cause crash in a relatively common scenario. + new_solutions.append(s) + continue if s is not None and not is_subtype(s, t.upper_bound): bound_satisfies_all = True for c in constraints: @@ -567,3 +572,10 @@ def pre_validate_solutions( continue new_solutions.append(s) return new_solutions + + +def is_callable_protocol(t: Type) -> bool: + proper_t = get_proper_type(t) + if isinstance(proper_t, Instance) and proper_t.type.is_protocol: + return "__call__" in proper_t.type.protocol_members + return False diff --git a/mypy/state.py b/mypy/state.py index cd3a360dd15f3..533dceeb1f240 100644 --- a/mypy/state.py +++ b/mypy/state.py @@ -24,5 +24,5 @@ def strict_optional_set(self, value: bool) -> Iterator[None]: self.strict_optional = saved -state: Final = StrictOptionalState(strict_optional=False) +state: Final = StrictOptionalState(strict_optional=True) find_occurrences: tuple[str, str] | None = None diff --git a/mypy/stats.py b/mypy/stats.py index b167a41b0e348..9c69a245741b9 100644 --- a/mypy/stats.py +++ b/mypy/stats.py @@ -203,7 +203,11 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: # Type variable definition -- not a real assignment. return if o.type: + # If there is an explicit type, don't visit the l.h.s. as an expression + # to avoid double-counting and mishandling special forms. self.type(o.type) + o.rvalue.accept(self) + return elif self.inferred and not self.all_nodes: # if self.all_nodes is set, lvalues will be visited later for lvalue in o.lvalues: diff --git a/mypy/strconv.py b/mypy/strconv.py index a96a27c45d758..2d595d4b67b0a 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -112,7 +112,7 @@ def visit_mypy_file(self, o: mypy.nodes.MypyFile) -> str: if o.path != "main": # Insert path. Normalize directory separators to / to unify test # case# output in all platforms. - a.insert(0, o.path.replace(os.sep, "/")) + a.insert(0, o.path.replace(os.getcwd() + os.sep, "").replace(os.sep, "/")) if o.ignored_lines: a.append("IgnoredLines(%s)" % ", ".join(str(line) for line in sorted(o.ignored_lines))) return self.dump(a, o) @@ -349,6 +349,8 @@ def type_param(self, p: mypy.nodes.TypeParam) -> list[Any]: a.append(p.upper_bound) if p.values: a.append(("Values", p.values)) + if p.default: + a.append(("Default", [p.default])) return [("TypeParam", a)] # Expressions diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 8c0a4dab696f9..434de0ea3bcb9 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -20,7 +20,7 @@ Sig: _TypeAlias = Tuple[str, str] -_TYPE_RE: Final = re.compile(r"^[a-zA-Z_][\w\[\], ]*(\.[a-zA-Z_][\w\[\], ]*)*$") +_TYPE_RE: Final = re.compile(r"^[a-zA-Z_][\w\[\], .\"\']*(\.[a-zA-Z_][\w\[\], ]*)*$") _ARG_NAME_RE: Final = re.compile(r"\**[A-Za-z_][A-Za-z0-9_]*$") @@ -76,6 +76,7 @@ class FunctionSig(NamedTuple): name: str args: list[ArgSig] ret_type: str | None + type_args: str = "" # TODO implement in stubgenc and remove the default def is_special_method(self) -> bool: return bool( @@ -141,9 +142,7 @@ def format_sig( retfield = " -> " + ret_type prefix = "async " if is_async else "" - sig = "{indent}{prefix}def {name}({args}){ret}:".format( - indent=indent, prefix=prefix, name=self.name, args=", ".join(args), ret=retfield - ) + sig = f"{indent}{prefix}def {self.name}{self.type_args}({', '.join(args)}){retfield}:" if docstring: suffix = f"\n{indent} {mypy.util.quote_docstring(docstring)}" else: diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 8478bd2135e4c..fdad5c2ddd895 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -54,6 +54,7 @@ import mypy.parse import mypy.traverser import mypy.util +import mypy.version from mypy.build import build from mypy.errors import CompileError, Errors from mypy.find_sources import InvalidSourceList, create_source_list @@ -105,6 +106,7 @@ StrExpr, TempNode, TupleExpr, + TypeAliasStmt, TypeInfo, UnaryExpr, Var, @@ -304,9 +306,26 @@ def visit_name_expr(self, node: NameExpr) -> str: def visit_member_expr(self, o: MemberExpr) -> str: return self._visit_ref_expr(o) - def visit_str_expr(self, node: StrExpr) -> str: + def _visit_literal_node( + self, node: StrExpr | BytesExpr | IntExpr | FloatExpr | ComplexExpr + ) -> str: return repr(node.value) + def visit_str_expr(self, node: StrExpr) -> str: + return self._visit_literal_node(node) + + def visit_bytes_expr(self, node: BytesExpr) -> str: + return f"b{self._visit_literal_node(node)}" + + def visit_int_expr(self, node: IntExpr) -> str: + return self._visit_literal_node(node) + + def visit_float_expr(self, node: FloatExpr) -> str: + return self._visit_literal_node(node) + + def visit_complex_expr(self, node: ComplexExpr) -> str: + return self._visit_literal_node(node) + def visit_index_expr(self, node: IndexExpr) -> str: base_fullname = self.stubgen.get_fullname(node.base) if base_fullname == "typing.Union": @@ -380,6 +399,9 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: for name in get_assigned_names(o.lvalues): self.names.add(name) + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + self.names.add(o.name.name) + def find_referenced_names(file: MypyFile) -> set[str]: finder = ReferenceFinder() @@ -450,7 +472,7 @@ def __init__( self._vars: list[list[str]] = [[]] # What was generated previously in the stub file. self._state = EMPTY - self._current_class: ClassDef | None = None + self._class_stack: list[ClassDef] = [] # Was the tree semantically analysed before? self.analyzed = analyzed # Short names of methods defined in the body of the current class @@ -458,6 +480,10 @@ def __init__( self.processing_enum = False self.processing_dataclass = False + @property + def _current_class(self) -> ClassDef | None: + return self._class_stack[-1] if self._class_stack else None + def visit_mypy_file(self, o: MypyFile) -> None: self.module_name = o.fullname # Current module being processed self.path = o.path @@ -489,7 +515,8 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: def get_default_function_sig(self, func_def: FuncDef, ctx: FunctionContext) -> FunctionSig: args = self._get_func_args(func_def, ctx) retname = self._get_func_return(func_def, ctx) - return FunctionSig(func_def.name, args, retname) + type_args = self.format_type_args(func_def) + return FunctionSig(func_def.name, args, retname, type_args) def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: args: list[ArgSig] = [] @@ -570,8 +597,8 @@ def _get_func_return(self, o: FuncDef, ctx: FunctionContext) -> str | None: if has_yield_expression(o) or has_yield_from_expression(o): generator_name = self.add_name("collections.abc.Generator") yield_name = "None" - send_name = "None" - return_name = "None" + send_name: str | None = None + return_name: str | None = None if has_yield_from_expression(o): yield_name = send_name = self.add_name("_typeshed.Incomplete") else: @@ -582,7 +609,14 @@ def _get_func_return(self, o: FuncDef, ctx: FunctionContext) -> str | None: send_name = self.add_name("_typeshed.Incomplete") if has_return_statement(o): return_name = self.add_name("_typeshed.Incomplete") - return f"{generator_name}[{yield_name}, {send_name}, {return_name}]" + if return_name is not None: + if send_name is None: + send_name = "None" + return f"{generator_name}[{yield_name}, {send_name}, {return_name}]" + elif send_name is not None: + return f"{generator_name}[{yield_name}, {send_name}]" + else: + return f"{generator_name}[{yield_name}]" if not has_return_statement(o) and o.abstract_status == NOT_ABSTRACT: return "None" return None @@ -621,12 +655,14 @@ def visit_func_def(self, o: FuncDef) -> None: if init_code: self.add(init_code) - if self._current_class is not None: + if self._class_stack: if len(o.arguments): self_var = o.arguments[0].variable.name else: self_var = "self" - class_info = ClassInfo(self._current_class.name, self_var) + class_info: ClassInfo | None = None + for class_def in self._class_stack: + class_info = ClassInfo(class_def.name, self_var, parent=class_info) else: class_info = None @@ -716,7 +752,7 @@ def get_fullname(self, expr: Expression) -> str: return self.resolve_name(name) def visit_class_def(self, o: ClassDef) -> None: - self._current_class = o + self._class_stack.append(o) self.method_names = find_method_names(o.defs.body) sep: int | None = None if self.is_top_level() and self._state != EMPTY: @@ -740,7 +776,8 @@ def visit_class_def(self, o: ClassDef) -> None: self.import_tracker.add_import("abc") self.import_tracker.require_name("abc") bases = f"({', '.join(base_types)})" if base_types else "" - self.add(f"{self._indent}class {o.name}{bases}:\n") + type_args = self.format_type_args(o) + self.add(f"{self._indent}class {o.name}{type_args}{bases}:\n") self.indent() if self._include_docstrings and o.docstring: docstring = mypy.util.quote_docstring(o.docstring) @@ -761,8 +798,8 @@ def visit_class_def(self, o: ClassDef) -> None: self._state = CLASS self.method_names = set() self.processing_dataclass = False + self._class_stack.pop(-1) self.processing_enum = False - self._current_class = None def get_base_types(self, cdef: ClassDef) -> list[str]: """Get list of base classes for a class.""" @@ -804,7 +841,8 @@ def get_base_types(self, cdef: ClassDef) -> list[str]: for name, value in cdef.keywords.items(): if name == "metaclass": continue # handled separately - base_types.append(f"{name}={value.accept(p)}") + processed_value = value.accept(p) or "..." # at least, don't crash + base_types.append(f"{name}={processed_value}") return base_types def get_class_decorators(self, cdef: ClassDef) -> list[str]: @@ -1075,6 +1113,16 @@ def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None: self.record_name(lvalue.name) self._vars[-1].append(lvalue.name) + def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None: + """Type aliases defined with the `type` keyword (PEP 695).""" + p = AliasPrinter(self) + name = o.name.name + rvalue = o.value.expr() + type_args = self.format_type_args(o) + self.add(f"{self._indent}type {name}{type_args} = {rvalue.accept(p)}\n") + self.record_name(name) + self._vars[-1].append(name) + def visit_if_stmt(self, o: IfStmt) -> None: # Ignore if __name__ == '__main__'. expr = o.expr[0] @@ -1751,7 +1799,9 @@ def generate_stubs(options: Options) -> None: def parse_options(args: list[str]) -> Options: - parser = argparse.ArgumentParser(prog="stubgen", usage=HEADER, description=DESCRIPTION) + parser = argparse.ArgumentParser( + prog="stubgen", usage=HEADER, description=DESCRIPTION, fromfile_prefix_chars="@" + ) parser.add_argument( "--ignore-errors", @@ -1772,7 +1822,7 @@ def parse_options(args: list[str]) -> Options: action="store_true", help="don't perform semantic analysis of sources, just parse them " "(only applies to Python modules, might affect quality of stubs. " - "Not compatible with --inspect)", + "Not compatible with --inspect-mode)", ) parser.add_argument( "--inspect-mode", @@ -1847,6 +1897,9 @@ def parse_options(args: list[str]) -> Options: dest="files", help="generate stubs for given files or directories", ) + parser.add_argument( + "--version", action="version", version="%(prog)s " + mypy.version.__version__ + ) ns = parser.parse_args(args) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index bacb68f6d1c73..1cd709b9d603f 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -12,7 +12,7 @@ import keyword import os.path from types import FunctionType, ModuleType -from typing import Any, Mapping +from typing import Any, Callable, Mapping from mypy.fastparse import parse_type_comment from mypy.moduleinspect import is_c_module @@ -252,6 +252,7 @@ def __init__( "Iterable", "Iterator", "List", + "Literal", "NamedTuple", "Optional", "Tuple", @@ -291,6 +292,8 @@ def get_default_function_sig(self, func: object, ctx: FunctionContext) -> Functi varargs = argspec.varargs kwargs = argspec.varkw annotations = argspec.annotations + kwonlyargs = argspec.kwonlyargs + kwonlydefaults = argspec.kwonlydefaults def get_annotation(key: str) -> str | None: if key not in annotations: @@ -303,27 +306,51 @@ def get_annotation(key: str) -> str | None: return argtype arglist: list[ArgSig] = [] + # Add the arguments to the signature - for i, arg in enumerate(args): - # Check if the argument has a default value - if defaults and i >= len(args) - len(defaults): - default_value = defaults[i - (len(args) - len(defaults))] - if arg in annotations: - argtype = annotations[arg] + def add_args( + args: list[str], get_default_value: Callable[[int, str], object | None] + ) -> None: + for i, arg in enumerate(args): + # Check if the argument has a default value + default_value = get_default_value(i, arg) + if default_value is not None: + if arg in annotations: + argtype = annotations[arg] + else: + argtype = self.get_type_annotation(default_value) + if argtype == "None": + # None is not a useful annotation, but we can infer that the arg + # is optional + incomplete = self.add_name("_typeshed.Incomplete") + argtype = f"{incomplete} | None" + + arglist.append(ArgSig(arg, argtype, default=True)) else: - argtype = self.get_type_annotation(default_value) - if argtype == "None": - # None is not a useful annotation, but we can infer that the arg - # is optional - incomplete = self.add_name("_typeshed.Incomplete") - argtype = f"{incomplete} | None" - arglist.append(ArgSig(arg, argtype, default=True)) + arglist.append(ArgSig(arg, get_annotation(arg), default=False)) + + def get_pos_default(i: int, _arg: str) -> Any | None: + if defaults and i >= len(args) - len(defaults): + return defaults[i - (len(args) - len(defaults))] else: - arglist.append(ArgSig(arg, get_annotation(arg), default=False)) + return None + + add_args(args, get_pos_default) # Add *args if present if varargs: arglist.append(ArgSig(f"*{varargs}", get_annotation(varargs))) + # if we have keyword only args, then wee need to add "*" + elif kwonlyargs: + arglist.append(ArgSig("*")) + + def get_kw_default(_i: int, arg: str) -> Any | None: + if kwonlydefaults: + return kwonlydefaults.get(arg) + else: + return None + + add_args(kwonlyargs, get_kw_default) # Add **kwargs if present if kwargs: @@ -760,7 +787,9 @@ def get_base_types(self, obj: type) -> list[str]: bases.append(base) return [self.strip_or_import(self.get_type_fullname(base)) for base in bases] - def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> None: + def generate_class_stub( + self, class_name: str, cls: type, output: list[str], parent_class: ClassInfo | None = None + ) -> None: """Generate stub for a single class using runtime introspection. The result lines will be appended to 'output'. If necessary, any @@ -781,7 +810,9 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> self.record_name(class_name) self.indent() - class_info = ClassInfo(class_name, "", getattr(cls, "__doc__", None), cls) + class_info = ClassInfo( + class_name, "", getattr(cls, "__doc__", None), cls, parent=parent_class + ) for attr, value in items: # use unevaluated descriptors when dealing with property inspection @@ -816,7 +847,7 @@ def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> class_info, ) elif inspect.isclass(value) and self.is_defined_in_module(value): - self.generate_class_stub(attr, value, types) + self.generate_class_stub(attr, value, types, parent_class=class_info) else: attrs.append((attr, value)) diff --git a/mypy/stubinfo.py b/mypy/stubinfo.py index 9d8dfbe43f37a..8d89a2a4beded 100644 --- a/mypy/stubinfo.py +++ b/mypy/stubinfo.py @@ -1,22 +1,37 @@ from __future__ import annotations -def is_legacy_bundled_package(prefix: str) -> bool: - return prefix in legacy_bundled_packages +def is_module_from_legacy_bundled_package(module: str) -> bool: + top_level = module.split(".", 1)[0] + return top_level in legacy_bundled_packages -def approved_stub_package_exists(prefix: str) -> bool: - return is_legacy_bundled_package(prefix) or prefix in non_bundled_packages +def stub_distribution_name(module: str) -> str | None: + top_level = module.split(".", 1)[0] + dist = legacy_bundled_packages.get(top_level) + if dist: + return dist + dist = non_bundled_packages_flat.get(top_level) + if dist: + return dist -def stub_distribution_name(prefix: str) -> str: - return legacy_bundled_packages.get(prefix) or non_bundled_packages[prefix] + if top_level in non_bundled_packages_namespace: + namespace = non_bundled_packages_namespace[top_level] + components = module.split(".") + for i in range(len(components), 0, -1): + module = ".".join(components[:i]) + dist = namespace.get(module) + if dist: + return dist + + return None # Stubs for these third-party packages used to be shipped with mypy. # # Map package name to PyPI stub distribution name. -legacy_bundled_packages = { +legacy_bundled_packages: dict[str, str] = { "aiofiles": "types-aiofiles", "bleach": "types-bleach", "boto": "types-boto", @@ -32,7 +47,6 @@ def stub_distribution_name(prefix: str) -> str: "docutils": "types-docutils", "first": "types-first", "gflags": "types-python-gflags", - "google.protobuf": "types-protobuf", "markdown": "types-Markdown", "mock": "types-mock", "OpenSSL": "types-pyOpenSSL", @@ -66,20 +80,17 @@ def stub_distribution_name(prefix: str) -> str: # include packages that have a release that includes PEP 561 type # information. # -# Package name can have one or two components ('a' or 'a.b'). -# # Note that these packages are omitted for now: # pika: typeshed's stubs are on PyPI as types-pika-ts. # types-pika already exists on PyPI, and is more complete in many ways, # but is a non-typeshed stubs package. -non_bundled_packages = { +non_bundled_packages_flat: dict[str, str] = { "MySQLdb": "types-mysqlclient", "PIL": "types-Pillow", "PyInstaller": "types-pyinstaller", "Xlib": "types-python-xlib", "aws_xray_sdk": "types-aws-xray-sdk", "babel": "types-babel", - "backports.ssl_match_hostname": "types-backports.ssl_match_hostname", "braintree": "types-braintree", "bs4": "types-beautifulsoup4", "bugbear": "types-flake8-bugbear", @@ -107,7 +118,6 @@ def stub_distribution_name(prefix: str) -> str: "flask_migrate": "types-Flask-Migrate", "fpdf": "types-fpdf2", "gdb": "types-gdb", - "google.cloud.ndb": "types-google-cloud-ndb", "hdbcli": "types-hdbcli", "html5lib": "types-html5lib", "httplib2": "types-httplib2", @@ -123,7 +133,6 @@ def stub_distribution_name(prefix: str) -> str: "oauthlib": "types-oauthlib", "openpyxl": "types-openpyxl", "opentracing": "types-opentracing", - "paho.mqtt": "types-paho-mqtt", "parsimonious": "types-parsimonious", "passlib": "types-passlib", "passpy": "types-passpy", @@ -171,3 +180,10 @@ def stub_distribution_name(prefix: str) -> str: "pandas": "pandas-stubs", # https://github.com/pandas-dev/pandas-stubs "lxml": "lxml-stubs", # https://github.com/lxml/lxml-stubs } + + +non_bundled_packages_namespace: dict[str, dict[str, str]] = { + "backports": {"backports.ssl_match_hostname": "types-backports.ssl_match_hostname"}, + "google": {"google.cloud.ndb": "types-google-cloud-ndb", "google.protobuf": "types-protobuf"}, + "paho": {"paho.mqtt": "types-paho-mqtt"}, +} diff --git a/mypy/stubtest.py b/mypy/stubtest.py index a7cde8b8fe6c8..36cd0a213d4d2 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -133,7 +133,7 @@ def is_missing_stub(self) -> bool: def is_positional_only_related(self) -> bool: """Whether or not the error is for something being (or not being) positional-only.""" # TODO: This is hacky, use error codes or something more resilient - return "leading double underscore" in self.message + return "should be positional" in self.message def get_description(self, concise: bool = False) -> str: """Returns a description of the error. @@ -348,6 +348,8 @@ def verify_mypyfile( # Only verify the contents of the stub's __all__ # if the stub actually defines __all__ yield from _verify_exported_names(object_path, stub, runtime_all_as_set) + else: + yield Error(object_path + ["__all__"], "is not present in stub", MISSING, runtime) else: runtime_all_as_set = None @@ -700,7 +702,7 @@ def _verify_arg_default_value( stub_default != runtime_arg.default # We want the types to match exactly, e.g. in case the stub has # True and the runtime has 1 (or vice versa). - or type(stub_default) is not type(runtime_arg.default) # noqa: E721 + or type(stub_default) is not type(runtime_arg.default) ) ): yield ( @@ -909,7 +911,7 @@ def _verify_signature( ): yield ( f'stub argument "{stub_arg.variable.name}" should be positional-only ' - f'(rename with a leading double underscore, i.e. "__{runtime_arg.name}")' + f'(add "/", e.g. "{runtime_arg.name}, /")' ) if ( runtime_arg.kind != inspect.Parameter.POSITIONAL_ONLY @@ -919,7 +921,7 @@ def _verify_signature( ): yield ( f'stub argument "{stub_arg.variable.name}" should be positional or keyword ' - "(remove leading double underscore)" + '(remove "/")' ) # Check unmatched positional args @@ -1224,6 +1226,9 @@ def _verify_readonly_property(stub: nodes.Decorator, runtime: Any) -> Iterator[s if isinstance(runtime, property): yield from _verify_final_method(stub.func, runtime.fget, MISSING) return + if isinstance(runtime, functools.cached_property): + yield from _verify_final_method(stub.func, runtime.func, MISSING) + return if inspect.isdatadescriptor(runtime): # It's enough like a property... return @@ -1888,6 +1893,8 @@ class _Arguments: custom_typeshed_dir: str | None check_typeshed: bool version: str + show_traceback: bool + pdb: bool # typeshed added a stub for __main__, but that causes stubtest to check itself @@ -1933,6 +1940,8 @@ def test_stubs(args: _Arguments, use_builtins_fixtures: bool = False) -> int: options.abs_custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir) options.config_file = args.mypy_config_file options.use_builtins_fixtures = use_builtins_fixtures + options.show_traceback = args.show_traceback + options.pdb = args.pdb if options.config_file: @@ -1941,6 +1950,18 @@ def set_strict_flags() -> None: # not needed yet parse_config_file(options, set_strict_flags, options.config_file, sys.stdout, sys.stderr) + def error_callback(msg: str) -> typing.NoReturn: + print(_style("error:", color="red", bold=True), msg) + sys.exit(1) + + def warning_callback(msg: str) -> None: + print(_style("warning:", color="yellow", bold=True), msg) + + options.process_error_codes(error_callback=error_callback) + options.process_incomplete_features( + error_callback=error_callback, warning_callback=warning_callback + ) + try: modules = build_stubs(modules, options, find_submodules=not args.check_typeshed) except StubtestFailure as stubtest_failure: @@ -2074,6 +2095,10 @@ def parse_options(args: list[str]) -> _Arguments: parser.add_argument( "--version", action="version", version="%(prog)s " + mypy.version.__version__ ) + parser.add_argument("--pdb", action="store_true", help="Invoke pdb on fatal error") + parser.add_argument( + "--show-traceback", "--tb", action="store_true", help="Show traceback on fatal error" + ) return parser.parse_args(args, namespace=_Arguments()) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 2f2db0dbbe535..c11843c57f2ad 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -16,17 +16,9 @@ import mypy.options from mypy.modulefinder import ModuleNotFoundReason from mypy.moduleinspect import InspectError, ModuleInspect +from mypy.nodes import PARAM_SPEC_KIND, TYPE_VAR_TUPLE_KIND, ClassDef, FuncDef, TypeAliasStmt from mypy.stubdoc import ArgSig, FunctionSig -from mypy.types import ( - AnyType, - NoneType, - RawExpressionType, - Type, - TypeList, - TypeStrVisitor, - UnboundType, - UnionType, -) +from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType # Modules that may fail when imported, or that may have side effects (fully qualified). NOT_IMPORTABLE_MODULES = () @@ -302,11 +294,12 @@ def args_str(self, args: Iterable[Type]) -> str: The main difference from list_str is the preservation of quotes for string arguments """ + types = ["builtins.bytes", "builtins.str"] res = [] for arg in args: arg_str = arg.accept(self) - if isinstance(arg, RawExpressionType): - res.append(repr(arg.literal_value)) + if isinstance(arg, UnboundType) and arg.original_str_fallback in types: + res.append(f"'{arg_str}'") else: res.append(arg_str) return ", ".join(res) @@ -314,12 +307,18 @@ def args_str(self, args: Iterable[Type]) -> str: class ClassInfo: def __init__( - self, name: str, self_var: str, docstring: str | None = None, cls: type | None = None + self, + name: str, + self_var: str, + docstring: str | None = None, + cls: type | None = None, + parent: ClassInfo | None = None, ) -> None: self.name = name self.self_var = self_var self.docstring = docstring self.cls = cls + self.parent = parent class FunctionContext: @@ -342,7 +341,13 @@ def __init__( def fullname(self) -> str: if self._fullname is None: if self.class_info: - self._fullname = f"{self.module_name}.{self.class_info.name}.{self.name}" + parents = [] + class_info: ClassInfo | None = self.class_info + while class_info is not None: + parents.append(class_info.name) + class_info = class_info.parent + namespace = ".".join(reversed(parents)) + self._fullname = f"{self.module_name}.{namespace}.{self.name}" else: self._fullname = f"{self.module_name}.{self.name}" return self._fullname @@ -785,6 +790,31 @@ def format_func_def( ) return lines + def format_type_args(self, o: TypeAliasStmt | FuncDef | ClassDef) -> str: + if not o.type_args: + return "" + p = AnnotationPrinter(self) + type_args_list: list[str] = [] + for type_arg in o.type_args: + if type_arg.kind == PARAM_SPEC_KIND: + prefix = "**" + elif type_arg.kind == TYPE_VAR_TUPLE_KIND: + prefix = "*" + else: + prefix = "" + if type_arg.upper_bound: + bound_or_values = f": {type_arg.upper_bound.accept(p)}" + elif type_arg.values: + bound_or_values = f": ({', '.join(v.accept(p) for v in type_arg.values)})" + else: + bound_or_values = "" + if type_arg.default: + default = f" = {type_arg.default.accept(p)}" + else: + default = "" + type_args_list.append(f"{prefix}{type_arg.name}{bound_or_values}{default}") + return "[" + ", ".join(type_args_list) + "]" + def print_annotation( self, t: Type, @@ -802,6 +832,8 @@ def is_not_in_all(self, name: str) -> bool: return False def is_private_name(self, name: str, fullname: str | None = None) -> bool: + if "__mypy-" in name: + return True # Never include mypy generated symbols if self._include_private: return False if fullname in self.EXTRA_EXPORTED: diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 649cbae4c8318..a26aaf798b58f 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -625,8 +625,8 @@ def visit_instance(self, left: Instance) -> bool: return is_named_instance(item, "builtins.object") if isinstance(right, LiteralType) and left.last_known_value is not None: return self._is_subtype(left.last_known_value, right) - if isinstance(right, CallableType): - # Special case: Instance can be a subtype of Callable. + if isinstance(right, FunctionLike): + # Special case: Instance can be a subtype of Callable / Overloaded. call = find_member("__call__", left, left, is_operator=True) if call: return self._is_subtype(call, right) @@ -886,31 +886,47 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: if isinstance(right, Instance): return self._is_subtype(left.fallback, right) elif isinstance(right, TypedDictType): + if left == right: + return True # Fast path if not left.names_are_wider_than(right): return False for name, l, r in left.zip(right): # TODO: should we pass on the full subtype_context here and below? - if self.proper_subtype: - check = is_same_type(l, r) + right_readonly = name in right.readonly_keys + if not right_readonly: + if self.proper_subtype: + check = is_same_type(l, r) + else: + check = is_equivalent( + l, + r, + ignore_type_params=self.subtype_context.ignore_type_params, + options=self.options, + ) else: - check = is_equivalent( - l, - r, - ignore_type_params=self.subtype_context.ignore_type_params, - options=self.options, - ) + # Read-only items behave covariantly + check = self._is_subtype(l, r) if not check: return False # Non-required key is not compatible with a required key since # indexing may fail unexpectedly if a required key is missing. - # Required key is not compatible with a non-required key since - # the prior doesn't support 'del' but the latter should support - # it. + # Required key is not compatible with a non-read-only non-required + # key since the prior doesn't support 'del' but the latter should + # support it. + # Required key is compatible with a read-only non-required key. + required_differ = (name in left.required_keys) != (name in right.required_keys) + if not right_readonly and required_differ: + return False + # Readonly fields check: + # + # A = TypedDict('A', {'x': ReadOnly[int]}) + # B = TypedDict('B', {'x': int}) + # def reset_x(b: B) -> None: + # b['x'] = 0 # - # NOTE: 'del' support is currently not implemented (#3550). We - # don't want to have to change subtyping after 'del' support - # lands so here we are anticipating that change. - if (name in left.required_keys) != (name in right.required_keys): + # So, `A` cannot be a subtype of `B`, while `B` can be a subtype of `A`, + # because you can use `B` everywhere you use `A`, but not the other way around. + if name in left.readonly_keys and name not in right.readonly_keys: return False # (NOTE: Fallbacks don't matter.) return True @@ -1928,6 +1944,8 @@ def restrict_subtype_away(t: Type, s: Type) -> Type: if (isinstance(get_proper_type(item), AnyType) or not covers_at_runtime(item, s)) ] return UnionType.make_union(new_items) + elif isinstance(p_t, TypeVarType): + return p_t.copy_modified(upper_bound=restrict_subtype_away(p_t.upper_bound, s)) elif covers_at_runtime(t, s): return UninhabitedType() else: @@ -2004,19 +2022,36 @@ def infer_variance(info: TypeInfo, i: int) -> bool: tvar = info.defn.type_vars[i] self_type = fill_typevars(info) for member in all_non_object_members(info): - if member in ("__init__", "__new__"): + # __mypy-replace is an implementation detail of the dataclass plugin + if member in ("__init__", "__new__", "__mypy-replace"): continue - node = info[member].node - if isinstance(node, Var) and node.type is None: - tv.variance = VARIANCE_NOT_READY - return False + if isinstance(self_type, TupleType): self_type = mypy.typeops.tuple_fallback(self_type) - flags = get_member_flags(member, self_type) - typ = find_member(member, self_type, self_type) settable = IS_SETTABLE in flags + + node = info[member].node + if isinstance(node, Var): + if node.type is None: + tv.variance = VARIANCE_NOT_READY + return False + if has_underscore_prefix(member): + # Special case to avoid false positives (and to pass conformance tests) + settable = False + + typ = find_member(member, self_type, self_type) if typ: + # It's okay for a method in a generic class with a contravariant type + # variable to return a generic instance of the class, if it doesn't involve + # variance (i.e. values of type variables are propagated). Our normal rules + # would disallow this. Replace such return types with 'Any' to allow this. + # + # This could probably be more lenient (e.g. allow self type be nested, don't + # require all type arguments to be identical to self_type), but this will + # hopefully cover the vast majority of such cases, including Self. + typ = erase_return_self_types(typ, self_type) + typ2 = expand_type(typ, {tvar.id: object_type}) if not is_subtype(typ, typ2): co = False @@ -2024,6 +2059,15 @@ def infer_variance(info: TypeInfo, i: int) -> bool: contra = False if settable: co = False + + # Infer variance from base classes, in case they have explicit variances + for base in info.bases: + base2 = expand_type(base, {tvar.id: object_type}) + if not is_subtype(base, base2): + co = False + if not is_subtype(base2, base): + contra = False + if co: v = COVARIANT elif contra: @@ -2036,6 +2080,10 @@ def infer_variance(info: TypeInfo, i: int) -> bool: return True +def has_underscore_prefix(name: str) -> bool: + return name.startswith("_") and not (name.startswith("__") and name.endswith("__")) + + def infer_class_variances(info: TypeInfo) -> bool: if not info.defn.type_args: return True @@ -2046,3 +2094,20 @@ def infer_class_variances(info: TypeInfo) -> bool: if not infer_variance(info, i): success = False return success + + +def erase_return_self_types(typ: Type, self_type: Instance) -> Type: + """If a typ is function-like and returns self_type, replace return type with Any.""" + proper_type = get_proper_type(typ) + if isinstance(proper_type, CallableType): + ret = get_proper_type(proper_type.ret_type) + if isinstance(ret, Instance) and ret == self_type: + return proper_type.copy_modified(ret_type=AnyType(TypeOfAny.implementation_artifact)) + elif isinstance(proper_type, Overloaded): + return Overloaded( + [ + cast(CallableType, erase_return_self_types(it, self_type)) + for it in proper_type.items + ] + ) + return typ diff --git a/mypy/test/data.py b/mypy/test/data.py index ee567afe2125e..bc17178d20e0b 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -304,7 +304,7 @@ def __init__( self.data = data self.line = line self.old_cwd: str | None = None - self.tmpdir: tempfile.TemporaryDirectory[str] | None = None + self.tmpdir: str | None = None def runtest(self) -> None: if self.skip: @@ -323,19 +323,19 @@ def runtest(self) -> None: save_dir: str | None = self.config.getoption("--save-failures-to", None) if save_dir: assert self.tmpdir is not None - target_dir = os.path.join(save_dir, os.path.basename(self.tmpdir.name)) + target_dir = os.path.join(save_dir, os.path.basename(self.tmpdir)) print(f"Copying data from test {self.name} to {target_dir}") if not os.path.isabs(target_dir): assert self.old_cwd target_dir = os.path.join(self.old_cwd, target_dir) - shutil.copytree(self.tmpdir.name, target_dir) + shutil.copytree(self.tmpdir, target_dir) raise def setup(self) -> None: parse_test_case(case=self) self.old_cwd = os.getcwd() - self.tmpdir = tempfile.TemporaryDirectory(prefix="mypy-test-") - os.chdir(self.tmpdir.name) + self.tmpdir = tempfile.mkdtemp(prefix="mypy-test-") + os.chdir(self.tmpdir) os.mkdir(test_temp_dir) # Precalculate steps for find_steps() @@ -371,10 +371,7 @@ def teardown(self) -> None: if self.old_cwd is not None: os.chdir(self.old_cwd) if self.tmpdir is not None: - try: - self.tmpdir.cleanup() - except OSError: - pass + shutil.rmtree(self.tmpdir, ignore_errors=True) self.old_cwd = None self.tmpdir = None diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index f532e77b82d31..4a80207d3ec7c 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -256,16 +256,9 @@ def local_sys_path_set() -> Iterator[None]: def testfile_pyversion(path: str) -> tuple[int, int]: - if path.endswith("python312.test"): - return 3, 12 - elif path.endswith("python311.test"): - return 3, 11 - elif path.endswith("python310.test"): - return 3, 10 - elif path.endswith("python39.test"): - return 3, 9 - elif path.endswith("python38.test"): - return 3, 8 + m = re.search(r"python3([0-9]+)\.test$", path) + if m: + return 3, int(m.group(1)) else: return defaults.PYTHON3_VERSION diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 5fba6192dcaf3..330e191af252d 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -45,6 +45,8 @@ typecheck_files.remove("check-python311.test") if sys.version_info < (3, 12): typecheck_files.remove("check-python312.test") +if sys.version_info < (3, 13): + typecheck_files.remove("check-python313.test") # Special tests for platforms with case-insensitive filesystems. if sys.platform not in ("darwin", "win32"): diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 800ba2dff0878..cb8672dfaf291 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -101,8 +101,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: if messages: a.extend(normalize_messages(messages)) - assert testcase.tmpdir - a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir.name)) + assert testcase.tmpdir is not None + a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir)) a.extend(self.maybe_inspect(step, server, main_src)) if server.fine_grained_manager: @@ -248,8 +248,8 @@ def perform_step( new_messages = normalize_messages(new_messages) a = new_messages - assert testcase.tmpdir - a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir.name)) + assert testcase.tmpdir is not None + a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir)) a.extend(self.maybe_inspect(step, server, main_src)) return a, triggered diff --git a/mypy/test/testmodulefinder.py b/mypy/test/testmodulefinder.py index 943913d6cadb1..65d9a66c5fa0d 100644 --- a/mypy/test/testmodulefinder.py +++ b/mypy/test/testmodulefinder.py @@ -53,12 +53,12 @@ def test__no_namespace_packages__find_a_in_pkg1(self) -> None: Find find pkg1/a.py for "a" with namespace_packages False. """ found_module = self.fmc_nons.find_module("a") - expected = os.path.join(data_path, "pkg1", "a.py") + expected = os.path.abspath(os.path.join(data_path, "pkg1", "a.py")) assert_equal(expected, found_module) def test__no_namespace_packages__find_b_in_pkg2(self) -> None: found_module = self.fmc_ns.find_module("b") - expected = os.path.join(data_path, "pkg2", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "pkg2", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_nsx_as_namespace_pkg_in_pkg1(self) -> None: @@ -67,7 +67,7 @@ def test__find_nsx_as_namespace_pkg_in_pkg1(self) -> None: the path to the first one found in mypypath. """ found_module = self.fmc_ns.find_module("nsx") - expected = os.path.join(data_path, "nsx-pkg1", "nsx") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg1", "nsx")) assert_equal(expected, found_module) def test__find_nsx_a_init_in_pkg1(self) -> None: @@ -75,7 +75,7 @@ def test__find_nsx_a_init_in_pkg1(self) -> None: Find nsx-pkg1/nsx/a/__init__.py for "nsx.a" in namespace mode. """ found_module = self.fmc_ns.find_module("nsx.a") - expected = os.path.join(data_path, "nsx-pkg1", "nsx", "a", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg1", "nsx", "a", "__init__.py")) assert_equal(expected, found_module) def test__find_nsx_b_init_in_pkg2(self) -> None: @@ -83,7 +83,7 @@ def test__find_nsx_b_init_in_pkg2(self) -> None: Find nsx-pkg2/nsx/b/__init__.py for "nsx.b" in namespace mode. """ found_module = self.fmc_ns.find_module("nsx.b") - expected = os.path.join(data_path, "nsx-pkg2", "nsx", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg2", "nsx", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_nsx_c_c_in_pkg3(self) -> None: @@ -91,7 +91,7 @@ def test__find_nsx_c_c_in_pkg3(self) -> None: Find nsx-pkg3/nsx/c/c.py for "nsx.c.c" in namespace mode. """ found_module = self.fmc_ns.find_module("nsx.c.c") - expected = os.path.join(data_path, "nsx-pkg3", "nsx", "c", "c.py") + expected = os.path.abspath(os.path.join(data_path, "nsx-pkg3", "nsx", "c", "c.py")) assert_equal(expected, found_module) def test__find_nsy_a__init_pyi(self) -> None: @@ -99,7 +99,7 @@ def test__find_nsy_a__init_pyi(self) -> None: Prefer nsy-pkg1/a/__init__.pyi file over __init__.py. """ found_module = self.fmc_ns.find_module("nsy.a") - expected = os.path.join(data_path, "nsy-pkg1", "nsy", "a", "__init__.pyi") + expected = os.path.abspath(os.path.join(data_path, "nsy-pkg1", "nsy", "a", "__init__.pyi")) assert_equal(expected, found_module) def test__find_nsy_b__init_py(self) -> None: @@ -109,7 +109,7 @@ def test__find_nsy_b__init_py(self) -> None: a package is preferred over a module. """ found_module = self.fmc_ns.find_module("nsy.b") - expected = os.path.join(data_path, "nsy-pkg2", "nsy", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "nsy-pkg2", "nsy", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_nsy_c_pyi(self) -> None: @@ -119,17 +119,17 @@ def test__find_nsy_c_pyi(self) -> None: .pyi is preferred over .py. """ found_module = self.fmc_ns.find_module("nsy.c") - expected = os.path.join(data_path, "nsy-pkg2", "nsy", "c.pyi") + expected = os.path.abspath(os.path.join(data_path, "nsy-pkg2", "nsy", "c.pyi")) assert_equal(expected, found_module) def test__find_a_in_pkg1(self) -> None: found_module = self.fmc_ns.find_module("a") - expected = os.path.join(data_path, "pkg1", "a.py") + expected = os.path.abspath(os.path.join(data_path, "pkg1", "a.py")) assert_equal(expected, found_module) def test__find_b_init_in_pkg2(self) -> None: found_module = self.fmc_ns.find_module("b") - expected = os.path.join(data_path, "pkg2", "b", "__init__.py") + expected = os.path.abspath(os.path.join(data_path, "pkg2", "b", "__init__.py")) assert_equal(expected, found_module) def test__find_d_nowhere(self) -> None: @@ -165,7 +165,7 @@ def setUp(self) -> None: self.fmc_nons = FindModuleCache(self.search_paths, fscache=None, options=options) def path(self, *parts: str) -> str: - return os.path.join(self.package_dir, *parts) + return os.path.abspath(os.path.join(self.package_dir, *parts)) def test__packages_with_ns(self) -> None: cases = [ @@ -214,7 +214,7 @@ def test__packages_with_ns(self) -> None: # A regular package with an installed set of stubs ("foo.bar", self.path("foo-stubs", "bar.pyi")), # A regular, non-site-packages module - ("a", os.path.join(data_path, "pkg1", "a.py")), + ("a", os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))), ] for module, expected in cases: template = "Find(" + module + ") got {}; expected {}" @@ -269,7 +269,7 @@ def test__packages_without_ns(self) -> None: # A regular package with an installed set of stubs ("foo.bar", self.path("foo-stubs", "bar.pyi")), # A regular, non-site-packages module - ("a", os.path.join(data_path, "pkg1", "a.py")), + ("a", os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))), ] for module, expected in cases: template = "Find(" + module + ") got {}; expected {}" diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index e215920a67979..074ccfb379d0c 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -25,6 +25,8 @@ class ParserSuite(DataSuite): files.remove("parse-python310.test") if sys.version_info < (3, 12): files.remove("parse-python312.test") + if sys.version_info < (3, 13): + files.remove("parse-python313.test") def run_case(self, testcase: DataDrivenTestCase) -> None: test_parser(testcase) @@ -43,6 +45,8 @@ def test_parser(testcase: DataDrivenTestCase) -> None: options.python_version = (3, 10) elif testcase.file.endswith("python312.test"): options.python_version = (3, 12) + elif testcase.file.endswith("python313.test"): + options.python_version = (3, 13) else: options.python_version = defaults.PYTHON3_VERSION diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 3669772854cb6..dffa1aa80c5db 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -38,6 +38,7 @@ from mypy.stubgenc import InspectionStubGenerator, infer_c_method_args from mypy.stubutil import ( ClassInfo, + FunctionContext, common_dir_prefix, infer_method_ret_type, remove_misplaced_type_comments, @@ -612,6 +613,16 @@ def test_common_dir_prefix_win(self) -> None: assert common_dir_prefix([r"foo\bar/x.pyi"]) == r"foo\bar" assert common_dir_prefix([r"foo/bar/x.pyi"]) == r"foo\bar" + def test_function_context_nested_classes(self) -> None: + ctx = FunctionContext( + module_name="spangle", + name="foo", + class_info=ClassInfo( + name="Nested", self_var="self", parent=ClassInfo(name="Parent", self_var="self") + ), + ) + assert ctx.fullname == "spangle.Parent.Nested.foo" + class StubgenHelpersSuite(unittest.TestCase): def test_is_blacklisted_path(self) -> None: @@ -845,6 +856,35 @@ class TestClassVariableCls: assert_equal(gen.get_imports().splitlines(), ["from typing import ClassVar"]) assert_equal(output, ["class C:", " x: ClassVar[int] = ..."]) + def test_non_c_generate_signature_with_kw_only_args(self) -> None: + class TestClass: + def test( + self, arg0: str, *, keyword_only: str, keyword_only_with_default: int = 7 + ) -> None: + pass + + output: list[str] = [] + mod = ModuleType(TestClass.__module__, "") + gen = InspectionStubGenerator(mod.__name__, known_modules=[mod.__name__], module=mod) + gen.is_c_module = False + gen.generate_function_stub( + "test", + TestClass.test, + output=output, + class_info=ClassInfo( + self_var="self", + cls=TestClass, + name="TestClass", + docstring=getattr(TestClass, "__doc__", None), + ), + ) + assert_equal( + output, + [ + "def test(self, arg0: str, *, keyword_only: str, keyword_only_with_default: int = ...) -> None: ..." + ], + ) + def test_generate_c_type_inheritance(self) -> None: class TestClass(KeyError): pass @@ -947,7 +987,7 @@ def test(cls, arg0: str) -> None: def test_generate_c_type_classmethod_with_overloads(self) -> None: class TestClass: @classmethod - def test(self, arg0: str) -> None: + def test(cls, arg0: str) -> None: """ test(cls, arg0: str) test(cls, arg0: int) @@ -1357,6 +1397,17 @@ def test_is_valid_type(self) -> None: assert is_valid_type("List[int]") assert is_valid_type("Dict[str, int]") assert is_valid_type("None") + assert is_valid_type("Literal[26]") + assert is_valid_type("Literal[0x1A]") + assert is_valid_type('Literal["hello world"]') + assert is_valid_type('Literal[b"hello world"]') + assert is_valid_type('Literal[u"hello world"]') + assert is_valid_type("Literal[True]") + assert is_valid_type("Literal[Color.RED]") + assert is_valid_type("Literal[None]") + assert is_valid_type( + 'Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]' + ) assert not is_valid_type("foo-bar") assert not is_valid_type("x->y") assert not is_valid_type("True") diff --git a/mypy/test/teststubinfo.py b/mypy/test/teststubinfo.py index eccee90244f3d..518194d35e1dd 100644 --- a/mypy/test/teststubinfo.py +++ b/mypy/test/teststubinfo.py @@ -2,11 +2,33 @@ import unittest -from mypy.stubinfo import is_legacy_bundled_package +from mypy.stubinfo import ( + is_module_from_legacy_bundled_package, + legacy_bundled_packages, + non_bundled_packages_flat, + stub_distribution_name, +) class TestStubInfo(unittest.TestCase): def test_is_legacy_bundled_packages(self) -> None: - assert not is_legacy_bundled_package("foobar_asdf") - assert is_legacy_bundled_package("pycurl") - assert is_legacy_bundled_package("dataclasses") + assert not is_module_from_legacy_bundled_package("foobar_asdf") + assert not is_module_from_legacy_bundled_package("PIL") + assert is_module_from_legacy_bundled_package("pycurl") + assert is_module_from_legacy_bundled_package("dataclasses") + + def test_stub_distribution_name(self) -> None: + assert stub_distribution_name("foobar_asdf") is None + assert stub_distribution_name("pycurl") == "types-pycurl" + assert stub_distribution_name("babel") == "types-babel" + assert stub_distribution_name("google.cloud.ndb") == "types-google-cloud-ndb" + assert stub_distribution_name("google.cloud.ndb.submodule") == "types-google-cloud-ndb" + assert stub_distribution_name("google.cloud.unknown") is None + assert stub_distribution_name("google.protobuf") == "types-protobuf" + assert stub_distribution_name("google.protobuf.submodule") == "types-protobuf" + assert stub_distribution_name("google") is None + + def test_period_in_top_level(self) -> None: + for packages in (non_bundled_packages_flat, legacy_bundled_packages): + for top_level_module in packages: + assert "." not in top_level_module diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 418308e2e65e6..fcbf07b4d3715 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -144,9 +144,9 @@ def __invert__(self: _T) -> _T: pass """ -def run_stubtest( +def run_stubtest_with_stderr( stub: str, runtime: str, options: list[str], config_file: str | None = None -) -> str: +) -> tuple[str, str]: with use_tmp_dir(TEST_MODULE_NAME) as tmp_dir: with open("builtins.pyi", "w") as f: f.write(stubtest_builtins_stub) @@ -163,13 +163,26 @@ def run_stubtest( f.write(config_file) options = options + ["--mypy-config-file", f"{TEST_MODULE_NAME}_config.ini"] output = io.StringIO() - with contextlib.redirect_stdout(output): + outerr = io.StringIO() + with contextlib.redirect_stdout(output), contextlib.redirect_stderr(outerr): test_stubs(parse_options([TEST_MODULE_NAME] + options), use_builtins_fixtures=True) - return remove_color_code( - output.getvalue() - # remove cwd as it's not available from outside - .replace(os.path.realpath(tmp_dir) + os.sep, "").replace(tmp_dir + os.sep, "") - ) + filtered_output = remove_color_code( + output.getvalue() + # remove cwd as it's not available from outside + .replace(os.path.realpath(tmp_dir) + os.sep, "").replace(tmp_dir + os.sep, "") + ) + filtered_outerr = remove_color_code( + outerr.getvalue() + # remove cwd as it's not available from outside + .replace(os.path.realpath(tmp_dir) + os.sep, "").replace(tmp_dir + os.sep, "") + ) + return filtered_output, filtered_outerr + + +def run_stubtest( + stub: str, runtime: str, options: list[str], config_file: str | None = None +) -> str: + return run_stubtest_with_stderr(stub, runtime, options, config_file)[0] class Case: @@ -893,6 +906,106 @@ class FineAndDandy: error=None, ) + @collect_cases + def test_cached_property(self) -> Iterator[Case]: + yield Case( + stub=""" + from functools import cached_property + class Good: + @cached_property + def read_only_attr(self) -> int: ... + @cached_property + def read_only_attr2(self) -> int: ... + """, + runtime=""" + import functools as ft + from functools import cached_property + class Good: + @cached_property + def read_only_attr(self): return 1 + @ft.cached_property + def read_only_attr2(self): return 1 + """, + error=None, + ) + yield Case( + stub=""" + from functools import cached_property + class Bad: + @cached_property + def f(self) -> int: ... + """, + runtime=""" + class Bad: + def f(self) -> int: return 1 + """, + error="Bad.f", + ) + yield Case( + stub=""" + from functools import cached_property + class GoodCachedAttr: + @cached_property + def f(self) -> int: ... + """, + runtime=""" + class GoodCachedAttr: + f = 1 + """, + error=None, + ) + yield Case( + stub=""" + from functools import cached_property + class BadCachedAttr: + @cached_property + def f(self) -> str: ... + """, + runtime=""" + class BadCachedAttr: + f = 1 + """, + error="BadCachedAttr.f", + ) + yield Case( + stub=""" + from functools import cached_property + from typing import final + class FinalGood: + @cached_property + @final + def attr(self) -> int: ... + """, + runtime=""" + from functools import cached_property + from typing import final + class FinalGood: + @cached_property + @final + def attr(self): + return 1 + """, + error=None, + ) + yield Case( + stub=""" + from functools import cached_property + class FinalBad: + @cached_property + def attr(self) -> int: ... + """, + runtime=""" + from functools import cached_property + from typing_extensions import final + class FinalBad: + @cached_property + @final + def attr(self): + return 1 + """, + error="FinalBad.attr", + ) + @collect_cases def test_var(self) -> Iterator[Case]: yield Case(stub="x1: int", runtime="x1 = 5", error=None) @@ -1154,9 +1267,9 @@ def test_enum(self) -> Iterator[Case]: yield Case( stub=""" class X(enum.Enum): - a: int - b: str - c: str + a = ... + b = "asdf" + c = "oops" """, runtime=""" class X(enum.Enum): @@ -1169,8 +1282,8 @@ class X(enum.Enum): yield Case( stub=""" class Flags1(enum.Flag): - a: int - b: int + a = ... + b = 2 def foo(x: Flags1 = ...) -> None: ... """, runtime=""" @@ -1184,8 +1297,8 @@ def foo(x=Flags1.a|Flags1.b): pass yield Case( stub=""" class Flags2(enum.Flag): - a: int - b: int + a = ... + b = 2 def bar(x: Flags2 | None = None) -> None: ... """, runtime=""" @@ -1199,8 +1312,8 @@ def bar(x=Flags2.a|Flags2.b): pass yield Case( stub=""" class Flags3(enum.Flag): - a: int - b: int + a = ... + b = 2 def baz(x: Flags3 | None = ...) -> None: ... """, runtime=""" @@ -1233,8 +1346,8 @@ class WeirdEnum(enum.Enum): yield Case( stub=""" class Flags4(enum.Flag): - a: int - b: int + a = 1 + b = 2 def spam(x: Flags4 | None = None) -> None: ... """, runtime=""" @@ -1249,7 +1362,7 @@ def spam(x=Flags4(0)): pass stub=""" from typing_extensions import Final, Literal class BytesEnum(bytes, enum.Enum): - a: bytes + a = b'foo' FOO: Literal[BytesEnum.a] BAR: Final = BytesEnum.a BAZ: BytesEnum @@ -1290,7 +1403,7 @@ def test_all_at_runtime_not_stub(self) -> Iterator[Case]: runtime=""" __all__ = [] Z = 5""", - error=None, + error="__all__", ) @collect_cases @@ -1330,7 +1443,7 @@ def h(x: str): ... runtime="", error="h", ) - yield Case(stub="", runtime="__all__ = []", error=None) # dummy case + yield Case(stub="", runtime="__all__ = []", error="__all__") # dummy case yield Case(stub="", runtime="__all__ += ['y']\ny = 5", error="y") yield Case(stub="", runtime="__all__ += ['g']\ndef g(): pass", error="g") # Here we should only check that runtime has B, since the stub explicitly re-exports it @@ -1784,7 +1897,7 @@ def test_good_literal(self) -> Iterator[Case]: import enum class Color(enum.Enum): - RED: int + RED = ... NUM: Literal[1] CHAR: Literal['a'] @@ -2477,6 +2590,46 @@ def test_config_file(self) -> None: output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) assert output == "Success: no issues found in 1 module\n" + def test_config_file_error_codes(self) -> None: + runtime = "temp = 5\n" + stub = "temp = SOME_GLOBAL_CONST" + output = run_stubtest(stub=stub, runtime=runtime, options=[]) + assert output == ( + "error: not checking stubs due to mypy build errors:\n" + 'test_module.pyi:1: error: Name "SOME_GLOBAL_CONST" is not defined [name-defined]\n' + ) + + config_file = "[mypy]\ndisable_error_code = name-defined\n" + output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + assert output == "Success: no issues found in 1 module\n" + + def test_config_file_error_codes_invalid(self) -> None: + runtime = "temp = 5\n" + stub = "temp: int\n" + config_file = "[mypy]\ndisable_error_code = not-a-valid-name\n" + output, outerr = run_stubtest_with_stderr( + stub=stub, runtime=runtime, options=[], config_file=config_file + ) + assert output == "Success: no issues found in 1 module\n" + assert outerr == ( + "test_module_config.ini: [mypy]: disable_error_code: " + "Invalid error code(s): not-a-valid-name\n" + ) + + def test_config_file_wrong_incomplete_feature(self) -> None: + runtime = "x = 1\n" + stub = "x: int\n" + config_file = "[mypy]\nenable_incomplete_feature = Unpack\n" + output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + assert output == ( + "warning: Warning: Unpack is already enabled by default\n" + "Success: no issues found in 1 module\n" + ) + + config_file = "[mypy]\nenable_incomplete_feature = not-a-valid-name\n" + with self.assertRaises(SystemExit): + run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + def test_no_modules(self) -> None: output = io.StringIO() with contextlib.redirect_stdout(output): diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 480fe38a90a71..175074a2b140a 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -4,7 +4,7 @@ from mypy.subtypes import is_subtype from mypy.test.helpers import Suite from mypy.test.typefixture import InterfaceTypeFixture, TypeFixture -from mypy.types import Instance, Type, UnpackType +from mypy.types import Instance, Type, UninhabitedType, UnpackType class SubtypingSuite(Suite): @@ -87,7 +87,7 @@ def test_basic_callable_subtyping(self) -> None: ) self.assert_strict_subtype( - self.fx.callable(self.fx.a, self.fx.nonet), self.fx.callable(self.fx.a, self.fx.a) + self.fx.callable(self.fx.a, UninhabitedType()), self.fx.callable(self.fx.a, self.fx.a) ) self.assert_unrelated( diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 0218d33cc1244..0380d1aa82d15 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -766,18 +766,19 @@ def test_type_vars(self) -> None: self.assert_join(self.fx.t, self.fx.s, self.fx.o) def test_none(self) -> None: - # Any type t joined with None results in t. - for t in [ - NoneType(), - self.fx.a, - self.fx.o, - UnboundType("x"), - self.fx.t, - self.tuple(), - self.callable(self.fx.a, self.fx.b), - self.fx.anyt, - ]: - self.assert_join(t, NoneType(), t) + with state.strict_optional_set(False): + # Any type t joined with None results in t. + for t in [ + NoneType(), + self.fx.a, + self.fx.o, + UnboundType("x"), + self.fx.t, + self.tuple(), + self.callable(self.fx.a, self.fx.b), + self.fx.anyt, + ]: + self.assert_join(t, NoneType(), t) def test_unbound_type(self) -> None: self.assert_join(UnboundType("x"), UnboundType("x"), self.fx.anyt) @@ -798,6 +799,9 @@ def test_unbound_type(self) -> None: def test_any_type(self) -> None: # Join against 'Any' type always results in 'Any'. + with state.strict_optional_set(False): + self.assert_join(NoneType(), self.fx.anyt, self.fx.anyt) + for t in [ self.fx.anyt, self.fx.a, @@ -834,7 +838,11 @@ def test_other_mixed_types(self) -> None: self.assert_join(t1, t2, self.fx.o) def test_simple_generics(self) -> None: - self.assert_join(self.fx.ga, self.fx.nonet, self.fx.ga) + with state.strict_optional_set(False): + self.assert_join(self.fx.ga, self.fx.nonet, self.fx.ga) + with state.strict_optional_set(True): + self.assert_join(self.fx.ga, self.fx.nonet, UnionType([self.fx.ga, NoneType()])) + self.assert_join(self.fx.ga, self.fx.anyt, self.fx.anyt) for t in [ @@ -1105,8 +1113,8 @@ def test_class_subtyping(self) -> None: self.assert_meet(self.fx.a, self.fx.o, self.fx.a) self.assert_meet(self.fx.a, self.fx.b, self.fx.b) self.assert_meet(self.fx.b, self.fx.o, self.fx.b) - self.assert_meet(self.fx.a, self.fx.d, NoneType()) - self.assert_meet(self.fx.b, self.fx.c, NoneType()) + self.assert_meet(self.fx.a, self.fx.d, UninhabitedType()) + self.assert_meet(self.fx.b, self.fx.c, UninhabitedType()) def test_tuples(self) -> None: self.assert_meet(self.tuple(), self.tuple(), self.tuple()) @@ -1114,13 +1122,15 @@ def test_tuples(self) -> None: self.assert_meet( self.tuple(self.fx.b, self.fx.c), self.tuple(self.fx.a, self.fx.d), - self.tuple(self.fx.b, NoneType()), + self.tuple(self.fx.b, UninhabitedType()), ) self.assert_meet( self.tuple(self.fx.a, self.fx.a), self.fx.std_tuple, self.tuple(self.fx.a, self.fx.a) ) - self.assert_meet(self.tuple(self.fx.a), self.tuple(self.fx.a, self.fx.a), NoneType()) + self.assert_meet( + self.tuple(self.fx.a), self.tuple(self.fx.a, self.fx.a), UninhabitedType() + ) def test_function_types(self) -> None: self.assert_meet( @@ -1143,7 +1153,7 @@ def test_function_types(self) -> None: def test_type_vars(self) -> None: self.assert_meet(self.fx.t, self.fx.t, self.fx.t) self.assert_meet(self.fx.s, self.fx.s, self.fx.s) - self.assert_meet(self.fx.t, self.fx.s, NoneType()) + self.assert_meet(self.fx.t, self.fx.s, UninhabitedType()) def test_none(self) -> None: self.assert_meet(NoneType(), NoneType(), NoneType()) @@ -1151,15 +1161,27 @@ def test_none(self) -> None: self.assert_meet(NoneType(), self.fx.anyt, NoneType()) # Any type t joined with None results in None, unless t is Any. - for t in [ - self.fx.a, - self.fx.o, - UnboundType("x"), - self.fx.t, - self.tuple(), - self.callable(self.fx.a, self.fx.b), - ]: - self.assert_meet(t, NoneType(), NoneType()) + with state.strict_optional_set(False): + for t in [ + self.fx.a, + self.fx.o, + UnboundType("x"), + self.fx.t, + self.tuple(), + self.callable(self.fx.a, self.fx.b), + ]: + self.assert_meet(t, NoneType(), NoneType()) + + with state.strict_optional_set(True): + self.assert_meet(self.fx.o, NoneType(), NoneType()) + for t in [ + self.fx.a, + UnboundType("x"), + self.fx.t, + self.tuple(), + self.callable(self.fx.a, self.fx.b), + ]: + self.assert_meet(t, NoneType(), UninhabitedType()) def test_unbound_type(self) -> None: self.assert_meet(UnboundType("x"), UnboundType("x"), self.fx.anyt) @@ -1197,28 +1219,28 @@ def test_simple_generics(self) -> None: self.assert_meet(self.fx.ga, self.fx.ga, self.fx.ga) self.assert_meet(self.fx.ga, self.fx.o, self.fx.ga) self.assert_meet(self.fx.ga, self.fx.gb, self.fx.gb) - self.assert_meet(self.fx.ga, self.fx.gd, self.fx.nonet) - self.assert_meet(self.fx.ga, self.fx.g2a, self.fx.nonet) + self.assert_meet(self.fx.ga, self.fx.gd, UninhabitedType()) + self.assert_meet(self.fx.ga, self.fx.g2a, UninhabitedType()) - self.assert_meet(self.fx.ga, self.fx.nonet, self.fx.nonet) + self.assert_meet(self.fx.ga, self.fx.nonet, UninhabitedType()) self.assert_meet(self.fx.ga, self.fx.anyt, self.fx.ga) for t in [self.fx.a, self.fx.t, self.tuple(), self.callable(self.fx.a, self.fx.b)]: - self.assert_meet(t, self.fx.ga, self.fx.nonet) + self.assert_meet(t, self.fx.ga, UninhabitedType()) def test_generics_with_multiple_args(self) -> None: self.assert_meet(self.fx.hab, self.fx.hab, self.fx.hab) self.assert_meet(self.fx.hab, self.fx.haa, self.fx.hab) - self.assert_meet(self.fx.hab, self.fx.had, self.fx.nonet) + self.assert_meet(self.fx.hab, self.fx.had, UninhabitedType()) self.assert_meet(self.fx.hab, self.fx.hbb, self.fx.hbb) def test_generics_with_inheritance(self) -> None: self.assert_meet(self.fx.gsab, self.fx.gb, self.fx.gsab) - self.assert_meet(self.fx.gsba, self.fx.gb, self.fx.nonet) + self.assert_meet(self.fx.gsba, self.fx.gb, UninhabitedType()) def test_generics_with_inheritance_and_shared_supertype(self) -> None: - self.assert_meet(self.fx.gsba, self.fx.gs2a, self.fx.nonet) - self.assert_meet(self.fx.gsab, self.fx.gs2a, self.fx.nonet) + self.assert_meet(self.fx.gsba, self.fx.gs2a, UninhabitedType()) + self.assert_meet(self.fx.gsab, self.fx.gs2a, UninhabitedType()) def test_generic_types_and_dynamic(self) -> None: self.assert_meet(self.fx.gdyn, self.fx.ga, self.fx.ga) @@ -1232,33 +1254,33 @@ def test_callables_with_dynamic(self) -> None: def test_meet_interface_types(self) -> None: self.assert_meet(self.fx.f, self.fx.f, self.fx.f) - self.assert_meet(self.fx.f, self.fx.f2, self.fx.nonet) + self.assert_meet(self.fx.f, self.fx.f2, UninhabitedType()) self.assert_meet(self.fx.f, self.fx.f3, self.fx.f3) def test_meet_interface_and_class_types(self) -> None: self.assert_meet(self.fx.o, self.fx.f, self.fx.f) - self.assert_meet(self.fx.a, self.fx.f, self.fx.nonet) + self.assert_meet(self.fx.a, self.fx.f, UninhabitedType()) self.assert_meet(self.fx.e, self.fx.f, self.fx.e) def test_meet_class_types_with_shared_interfaces(self) -> None: # These have nothing special with respect to meets, unlike joins. These # are for completeness only. - self.assert_meet(self.fx.e, self.fx.e2, self.fx.nonet) - self.assert_meet(self.fx.e2, self.fx.e3, self.fx.nonet) + self.assert_meet(self.fx.e, self.fx.e2, UninhabitedType()) + self.assert_meet(self.fx.e2, self.fx.e3, UninhabitedType()) def test_meet_with_generic_interfaces(self) -> None: fx = InterfaceTypeFixture() self.assert_meet(fx.gfa, fx.m1, fx.m1) self.assert_meet(fx.gfa, fx.gfa, fx.gfa) - self.assert_meet(fx.gfb, fx.m1, fx.nonet) + self.assert_meet(fx.gfb, fx.m1, UninhabitedType()) def test_type_type(self) -> None: self.assert_meet(self.fx.type_a, self.fx.type_b, self.fx.type_b) self.assert_meet(self.fx.type_b, self.fx.type_any, self.fx.type_b) self.assert_meet(self.fx.type_b, self.fx.type_type, self.fx.type_b) - self.assert_meet(self.fx.type_b, self.fx.type_c, self.fx.nonet) - self.assert_meet(self.fx.type_c, self.fx.type_d, self.fx.nonet) + self.assert_meet(self.fx.type_b, self.fx.type_c, self.fx.type_never) + self.assert_meet(self.fx.type_c, self.fx.type_d, self.fx.type_never) self.assert_meet(self.fx.type_type, self.fx.type_any, self.fx.type_any) self.assert_meet(self.fx.type_b, self.fx.anyt, self.fx.type_b) diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index 5a813f70117c6..d6c904732b179 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -216,6 +216,7 @@ def make_type_var( self.type_d = TypeType.make_normalized(self.d) self.type_t = TypeType.make_normalized(self.t) self.type_any = TypeType.make_normalized(self.anyt) + self.type_never = TypeType.make_normalized(UninhabitedType()) self._add_bool_dunder(self.bool_type_info) self._add_bool_dunder(self.ai) diff --git a/mypy/traverser.py b/mypy/traverser.py index 6f162c9ec5760..9c333c587f7c4 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -958,9 +958,3 @@ def visit_assignment_stmt(self, stmt: AssignmentStmt) -> None: def visit_yield_from_expr(self, expr: YieldFromExpr) -> None: self.yield_from_expressions.append((expr, self.in_assignment)) - - -def all_yield_from_expressions(node: Node) -> list[tuple[YieldFromExpr, bool]]: - v = YieldFromCollector() - node.accept(v) - return v.yield_from_expressions diff --git a/mypy/treetransform.py b/mypy/treetransform.py index bb34d8de28844..aafa4e95d530d 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -276,7 +276,7 @@ def visit_nonlocal_decl(self, node: NonlocalDecl) -> NonlocalDecl: return NonlocalDecl(node.names.copy()) def visit_block(self, node: Block) -> Block: - return Block(self.statements(node.body)) + return Block(self.statements(node.body), is_unreachable=node.is_unreachable) def visit_decorator(self, node: Decorator) -> Decorator: # Note that a Decorator must be transformed to a Decorator. diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index d0876629fc082..8aac7e5c2bbdc 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -181,8 +181,26 @@ class TypeTranslator(TypeVisitor[Type]): Subclass this and override some methods to implement a non-trivial transformation. + + We cache the results of certain translations to avoid + massively expanding the sizes of types. """ + def __init__(self, cache: dict[Type, Type] | None = None) -> None: + # For deduplication of results + self.cache = cache + + def get_cached(self, t: Type) -> Type | None: + if self.cache is None: + return None + return self.cache.get(t) + + def set_cached(self, orig: Type, new: Type) -> None: + if self.cache is None: + # Minor optimization: construct lazily + self.cache = {} + self.cache[orig] = new + def visit_unbound_type(self, t: UnboundType) -> Type: return t @@ -213,6 +231,7 @@ def visit_instance(self, t: Instance) -> Type: line=t.line, column=t.column, last_known_value=last_known_value, + extra_attrs=t.extra_attrs, ) def visit_type_var(self, t: TypeVarType) -> Type: @@ -250,15 +269,21 @@ def visit_tuple_type(self, t: TupleType) -> Type: ) def visit_typeddict_type(self, t: TypedDictType) -> Type: + # Use cache to avoid O(n**2) or worse expansion of types during translation + if cached := self.get_cached(t): + return cached items = {item_name: item_type.accept(self) for (item_name, item_type) in t.items.items()} - return TypedDictType( + result = TypedDictType( items, t.required_keys, + t.readonly_keys, # TODO: This appears to be unsafe. cast(Any, t.fallback.accept(self)), t.line, t.column, ) + self.set_cached(t, result) + return result def visit_literal_type(self, t: LiteralType) -> Type: fallback = t.fallback.accept(self) @@ -266,12 +291,21 @@ def visit_literal_type(self, t: LiteralType) -> Type: return LiteralType(value=t.value, fallback=fallback, line=t.line, column=t.column) def visit_union_type(self, t: UnionType) -> Type: - return UnionType( + # Use cache to avoid O(n**2) or worse expansion of types during translation + # (only for large unions, since caching adds overhead) + use_cache = len(t.items) > 3 + if use_cache and (cached := self.get_cached(t)): + return cached + + result = UnionType( self.translate_types(t.items), t.line, t.column, uses_pep604_syntax=t.uses_pep604_syntax, ) + if use_cache: + self.set_cached(t, result) + return result def translate_types(self, types: Iterable[Type]) -> list[Type]: return [t.accept(self) for t in types] @@ -381,8 +415,6 @@ def visit_typeddict_type(self, t: TypedDictType) -> T: return self.query_types(t.items.values()) def visit_raw_expression_type(self, t: RawExpressionType) -> T: - if t.node is not None: - return t.node.accept(self) return self.strategy([]) def visit_literal_type(self, t: LiteralType) -> T: @@ -523,8 +555,6 @@ def visit_typeddict_type(self, t: TypedDictType) -> bool: return self.query_types(list(t.items.values())) def visit_raw_expression_type(self, t: RawExpressionType) -> bool: - if t.node is not None: - return t.node.accept(self) return self.default def visit_literal_type(self, t: LiteralType) -> bool: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 6651af7dad4f0..2f85e83bb3c34 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -10,7 +10,11 @@ from mypy import errorcodes as codes, message_registry, nodes from mypy.errorcodes import ErrorCode from mypy.expandtype import expand_type -from mypy.message_registry import INVALID_PARAM_SPEC_LOCATION, INVALID_PARAM_SPEC_LOCATION_NOTE +from mypy.message_registry import ( + INVALID_PARAM_SPEC_LOCATION, + INVALID_PARAM_SPEC_LOCATION_NOTE, + TYPEDDICT_OVERRIDE_MERGE, +) from mypy.messages import ( MessageBuilder, format_type, @@ -25,10 +29,12 @@ ARG_POS, ARG_STAR, ARG_STAR2, + MISSING_FALLBACK, SYMBOL_FUNCBASE_TYPES, ArgKind, Context, Decorator, + ImportFrom, MypyFile, ParamSpecExpr, PlaceholderNode, @@ -43,7 +49,7 @@ check_arg_names, get_nongen_builtins, ) -from mypy.options import Options +from mypy.options import INLINE_TYPEDDICT, Options from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface from mypy.semanal_shared import ( SemanticAnalyzerCoreInterface, @@ -78,6 +84,7 @@ PlaceholderType, ProperType, RawExpressionType, + ReadOnlyType, RequiredType, SyntheticTypeVisitor, TrivialSyntheticTypeTranslator, @@ -142,6 +149,7 @@ def analyze_type_alias( tvar_scope: TypeVarLikeScope, plugin: Plugin, options: Options, + cur_mod_node: MypyFile, is_typeshed_stub: bool, allow_placeholder: bool = False, in_dynamic_func: bool = False, @@ -161,6 +169,7 @@ def analyze_type_alias( tvar_scope, plugin, options, + cur_mod_node, is_typeshed_stub, defining_alias=True, allow_placeholder=allow_placeholder, @@ -171,7 +180,7 @@ def analyze_type_alias( ) analyzer.in_dynamic_func = in_dynamic_func analyzer.global_scope = global_scope - res = type.accept(analyzer) + res = analyzer.anal_type(type, nested=False) return res, analyzer.aliases_used @@ -207,6 +216,7 @@ def __init__( tvar_scope: TypeVarLikeScope, plugin: Plugin, options: Options, + cur_mod_node: MypyFile, is_typeshed_stub: bool, *, defining_alias: bool = False, @@ -214,7 +224,7 @@ def __init__( allow_tuple_literal: bool = False, allow_unbound_tvars: bool = False, allow_placeholder: bool = False, - allow_required: bool = False, + allow_typed_dict_special_forms: bool = False, allow_param_spec_literals: bool = False, allow_unpack: bool = False, report_invalid_types: bool = True, @@ -248,7 +258,7 @@ def __init__( # If false, record incomplete ref if we generate PlaceholderType. self.allow_placeholder = allow_placeholder # Are we in a context where Required[] is allowed? - self.allow_required = allow_required + self.allow_typed_dict_special_forms = allow_typed_dict_special_forms # Are we in a context where ParamSpec literals are allowed? self.allow_param_spec_literals = allow_param_spec_literals # Are we in context where literal "..." specifically is allowed? @@ -260,6 +270,7 @@ def __init__( self.report_invalid_types = report_invalid_types self.plugin = plugin self.options = options + self.cur_mod_node = cur_mod_node self.is_typeshed_stub = is_typeshed_stub # Names of type aliases encountered while analysing a type will be collected here. self.aliases_used: set[str] = set() @@ -677,9 +688,11 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ code=codes.VALID_TYPE, ) return AnyType(TypeOfAny.from_error) - return self.anal_type(t.args[0]) + return self.anal_type( + t.args[0], allow_typed_dict_special_forms=self.allow_typed_dict_special_forms + ) elif fullname in ("typing_extensions.Required", "typing.Required"): - if not self.allow_required: + if not self.allow_typed_dict_special_forms: self.fail( "Required[] can be only used in a TypedDict definition", t, @@ -691,9 +704,11 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ "Required[] must have exactly one type argument", t, code=codes.VALID_TYPE ) return AnyType(TypeOfAny.from_error) - return RequiredType(self.anal_type(t.args[0]), required=True) + return RequiredType( + self.anal_type(t.args[0], allow_typed_dict_special_forms=True), required=True + ) elif fullname in ("typing_extensions.NotRequired", "typing.NotRequired"): - if not self.allow_required: + if not self.allow_typed_dict_special_forms: self.fail( "NotRequired[] can be only used in a TypedDict definition", t, @@ -705,7 +720,23 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ "NotRequired[] must have exactly one type argument", t, code=codes.VALID_TYPE ) return AnyType(TypeOfAny.from_error) - return RequiredType(self.anal_type(t.args[0]), required=False) + return RequiredType( + self.anal_type(t.args[0], allow_typed_dict_special_forms=True), required=False + ) + elif fullname in ("typing_extensions.ReadOnly", "typing.ReadOnly"): + if not self.allow_typed_dict_special_forms: + self.fail( + "ReadOnly[] can be only used in a TypedDict definition", + t, + code=codes.VALID_TYPE, + ) + return AnyType(TypeOfAny.from_error) + if len(t.args) != 1: + self.fail( + '"ReadOnly[]" must have exactly one type argument', t, code=codes.VALID_TYPE + ) + return AnyType(TypeOfAny.from_error) + return ReadOnlyType(self.anal_type(t.args[0], allow_typed_dict_special_forms=True)) elif ( self.anal_type_guard_arg(t, fullname) is not None or self.anal_type_is_arg(t, fullname) is not None @@ -747,6 +778,21 @@ def get_omitted_any(self, typ: Type, fullname: str | None = None) -> AnyType: disallow_any = not self.is_typeshed_stub and self.options.disallow_any_generics return get_omitted_any(disallow_any, self.fail, self.note, typ, self.options, fullname) + def check_and_warn_deprecated(self, info: TypeInfo, ctx: Context) -> None: + """Similar logic to `TypeChecker.check_deprecated` and `TypeChecker.warn_deprecated.""" + + if ( + (deprecated := info.deprecated) + and not self.is_typeshed_stub + and not (self.api.type and (self.api.type.fullname == info.fullname)) + ): + for imp in self.cur_mod_node.imports: + if isinstance(imp, ImportFrom) and any(info.name == n[0] for n in imp.names): + break + else: + warn = self.note if self.options.report_deprecated_as_note else self.fail + warn(deprecated, ctx, code=codes.DEPRECATED) + def analyze_type_with_type_info( self, info: TypeInfo, args: Sequence[Type], ctx: Context, empty_tuple_index: bool ) -> Type: @@ -755,6 +801,8 @@ def analyze_type_with_type_info( This handles simple cases like 'int', 'modname.UserClass[str]', etc. """ + self.check_and_warn_deprecated(info, ctx) + if len(args) > 0 and info.fullname == "builtins.tuple": fallback = Instance(info, [AnyType(TypeOfAny.special_form)], ctx.line) return TupleType(self.anal_array(args, allow_unpack=True), fallback, ctx.line) @@ -944,14 +992,12 @@ def analyze_unbound_type_without_type_info( message = 'Type variable "{}" is unbound' short = name.split(".")[-1] notes.append( - ( - '(Hint: Use "Generic[{}]" or "Protocol[{}]" base class' - ' to bind "{}" inside a class)' - ).format(short, short, short) + f'(Hint: Use "Generic[{short}]" or "Protocol[{short}]" base class' + f' to bind "{short}" inside a class)' ) notes.append( - '(Hint: Use "{}" in function signature to bind "{}"' - " inside a function)".format(short, short) + f'(Hint: Use "{short}" in function signature ' + f'to bind "{short}" inside a function)' ) else: message = 'Cannot interpret reference "{}" as a type' @@ -1107,7 +1153,6 @@ def visit_callable_type( return ret def anal_type_guard(self, t: Type) -> Type | None: - t = t.resolve_string_annotation() if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) if sym is not None and sym.node is not None: @@ -1126,7 +1171,6 @@ def anal_type_guard_arg(self, t: UnboundType, fullname: str) -> Type | None: return None def anal_type_is(self, t: Type) -> Type | None: - t = t.resolve_string_annotation() if isinstance(t, UnboundType): sym = self.lookup_qualified(t.name, t) if sym is not None and sym.node is not None: @@ -1144,7 +1188,6 @@ def anal_type_is_arg(self, t: UnboundType, fullname: str) -> Type | None: def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: """Analyze signature argument type for *args and **kwargs argument.""" - t = t.resolve_string_annotation() if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args: components = t.name.split(".") tvar_name = ".".join(components[:-1]) @@ -1220,10 +1263,52 @@ def visit_tuple_type(self, t: TupleType) -> Type: return TupleType(self.anal_array(t.items, allow_unpack=True), fallback, t.line) def visit_typeddict_type(self, t: TypedDictType) -> Type: - items = { - item_name: self.anal_type(item_type) for (item_name, item_type) in t.items.items() - } - return TypedDictType(items, set(t.required_keys), t.fallback) + req_keys = set() + readonly_keys = set() + items = {} + for item_name, item_type in t.items.items(): + # TODO: rework + analyzed = self.anal_type(item_type, allow_typed_dict_special_forms=True) + if isinstance(analyzed, RequiredType): + if analyzed.required: + req_keys.add(item_name) + analyzed = analyzed.item + else: + # Keys are required by default. + req_keys.add(item_name) + if isinstance(analyzed, ReadOnlyType): + readonly_keys.add(item_name) + analyzed = analyzed.item + items[item_name] = analyzed + if t.fallback.type is MISSING_FALLBACK: # anonymous/inline TypedDict + if INLINE_TYPEDDICT not in self.options.enable_incomplete_feature: + self.fail( + "Inline TypedDict is experimental," + " must be enabled with --enable-incomplete-feature=InlineTypedDict", + t, + ) + required_keys = req_keys + fallback = self.named_type("typing._TypedDict") + for typ in t.extra_items_from: + analyzed = self.analyze_type(typ) + p_analyzed = get_proper_type(analyzed) + if not isinstance(p_analyzed, TypedDictType): + if not isinstance(p_analyzed, (AnyType, PlaceholderType)): + self.fail("Can only merge-in other TypedDict", t, code=codes.VALID_TYPE) + continue + for sub_item_name, sub_item_type in p_analyzed.items.items(): + if sub_item_name in items: + self.fail(TYPEDDICT_OVERRIDE_MERGE.format(sub_item_name), t) + continue + items[sub_item_name] = sub_item_type + if sub_item_name in p_analyzed.required_keys: + req_keys.add(sub_item_name) + if sub_item_name in p_analyzed.readonly_keys: + readonly_keys.add(sub_item_name) + else: + required_keys = t.required_keys + fallback = t.fallback + return TypedDictType(items, required_keys, readonly_keys, fallback, t.line, t.column) def visit_raw_expression_type(self, t: RawExpressionType) -> Type: # We should never see a bare Literal. We synthesize these raw literals @@ -1235,8 +1320,6 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type: # make signatures like "foo(x: 20) -> None" legal, we can change # this method so it generates and returns an actual LiteralType # instead. - if t.node is not None: - return t.node.accept(self) if self.report_invalid_types: if t.base_type_name in ("builtins.int", "builtins.bool"): @@ -1499,7 +1582,6 @@ def analyze_callable_args( invalid_unpacks: list[Type] = [] second_unpack_last = False for i, arg in enumerate(arglist.items): - arg = arg.resolve_string_annotation() if isinstance(arg, CallableArgument): args.append(arg.typ) names.append(arg.name) @@ -1580,6 +1662,22 @@ def analyze_literal_type(self, t: UnboundType) -> Type: return UnionType.make_union(output, line=t.line) def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] | None: + # This UnboundType was originally defined as a string. + if ( + isinstance(arg, ProperType) + and isinstance(arg, (UnboundType, UnionType)) + and arg.original_str_expr is not None + ): + assert arg.original_str_fallback is not None + return [ + LiteralType( + value=arg.original_str_expr, + fallback=self.named_type(arg.original_str_fallback), + line=arg.line, + column=arg.column, + ) + ] + # If arg is an UnboundType that was *not* originally defined as # a string, try expanding it in case it's a type alias or something. if isinstance(arg, UnboundType): @@ -1761,11 +1859,12 @@ def anal_type( allow_param_spec: bool = False, allow_unpack: bool = False, allow_ellipsis: bool = False, + allow_typed_dict_special_forms: bool = False, ) -> Type: if nested: self.nesting_level += 1 - old_allow_required = self.allow_required - self.allow_required = False + old_allow_typed_dict_special_forms = self.allow_typed_dict_special_forms + self.allow_typed_dict_special_forms = allow_typed_dict_special_forms old_allow_ellipsis = self.allow_ellipsis self.allow_ellipsis = allow_ellipsis old_allow_unpack = self.allow_unpack @@ -1775,7 +1874,7 @@ def anal_type( finally: if nested: self.nesting_level -= 1 - self.allow_required = old_allow_required + self.allow_typed_dict_special_forms = old_allow_typed_dict_special_forms self.allow_ellipsis = old_allow_ellipsis self.allow_unpack = old_allow_unpack if ( @@ -2178,7 +2277,8 @@ def set_any_tvars( env[tv.id] = arg t = TypeAliasType(node, args, newline, newcolumn) if not has_type_var_tuple_type: - fixed = expand_type(t, env) + with state.strict_optional_set(options.strict_optional): + fixed = expand_type(t, env) assert isinstance(fixed, TypeAliasType) t.args = fixed.args @@ -2201,15 +2301,6 @@ def set_any_tvars( return t -def flatten_tvars(lists: list[list[T]]) -> list[T]: - result: list[T] = [] - for lst in lists: - for item in lst: - if item not in result: - result.append(item) - return result - - class DivergingAliasDetector(TrivialSyntheticTypeTranslator): """See docstring of detect_diverging_alias() for details.""" @@ -2220,6 +2311,7 @@ def __init__( lookup: Callable[[str, Context], SymbolTableNode | None], scope: TypeVarLikeScope, ) -> None: + super().__init__() self.seen_nodes = seen_nodes self.lookup = lookup self.scope = scope @@ -2564,8 +2656,7 @@ def visit_typeddict_type(self, t: TypedDictType) -> None: self.process_types(list(t.items.values())) def visit_raw_expression_type(self, t: RawExpressionType) -> None: - if t.node is not None: - t.node.accept(self) + pass def visit_literal_type(self, t: LiteralType) -> None: pass @@ -2610,6 +2701,7 @@ class TypeVarDefaultTranslator(TrivialSyntheticTypeTranslator): def __init__( self, api: SemanticAnalyzerInterface, tvar_expr_name: str, context: Context ) -> None: + super().__init__() self.api = api self.tvar_expr_name = tvar_expr_name self.context = context diff --git a/mypy/typeops.py b/mypy/typeops.py index 4fe187f811ca3..1ffb8207ec0d9 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -14,6 +14,7 @@ from mypy.expandtype import expand_type, expand_type_by_instance from mypy.maptype import map_instance_to_supertype from mypy.nodes import ( + ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, @@ -30,7 +31,6 @@ ) from mypy.state import state from mypy.types import ( - ENUM_REMOVED_PROPS, AnyType, CallableType, ExtraAttrs, @@ -114,7 +114,11 @@ def tuple_fallback(typ: TupleType) -> Instance: else: items.append(item) return Instance( - info, [make_simplified_union(items)], extra_attrs=typ.partial_fallback.extra_attrs + info, + # Note: flattening recursive unions is dangerous, since it can fool recursive + # types optimization in subtypes.py and go into infinite recursion. + [make_simplified_union(items, handle_recursive=False)], + extra_attrs=typ.partial_fallback.extra_attrs, ) @@ -301,21 +305,41 @@ class B(A): pass """ if isinstance(method, Overloaded): - items = [ - bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items - ] + items = [] + original_type = get_proper_type(original_type) + for c in method.items: + if isinstance(original_type, Instance): + # Filter based on whether declared self type can match actual object type. + # For example, if self has type C[int] and method is accessed on a C[str] value, + # omit this item. This is best effort since bind_self can be called in many + # contexts, and doing complete validation might trigger infinite recursion. + # + # Note that overload item filtering normally happens elsewhere. This is needed + # at least during constraint inference. + keep = is_valid_self_type_best_effort(c, original_type) + else: + keep = True + if keep: + items.append(bind_self(c, original_type, is_classmethod, ignore_instances)) + if len(items) == 0: + # If no item matches, returning all items helps avoid some spurious errors + items = [ + bind_self(c, original_type, is_classmethod, ignore_instances) for c in method.items + ] return cast(F, Overloaded(items)) assert isinstance(method, CallableType) func = method if not func.arg_types: # Invalid method, return something. return cast(F, func) - if func.arg_kinds[0] == ARG_STAR: + if func.arg_kinds[0] in (ARG_STAR, ARG_STAR2): # The signature is of the form 'def foo(*args, ...)'. # In this case we shouldn't drop the first arg, # since func will be absorbed by the *args. - # TODO: infer bounds on the type of *args? + + # In the case of **kwargs we should probably emit an error, but + # for now we simply skip it, to avoid crashes down the line. return cast(F, func) self_param_type = get_proper_type(func.arg_types[0]) @@ -373,6 +397,43 @@ class B(A): pass return cast(F, res) +def is_valid_self_type_best_effort(c: CallableType, self_type: Instance) -> bool: + """Quickly check if self_type might match the self in a callable. + + Avoid performing any complex type operations. This is performance-critical. + + Default to returning True if we don't know (or it would be too expensive). + """ + if ( + self_type.args + and c.arg_types + and isinstance((arg_type := get_proper_type(c.arg_types[0])), Instance) + and c.arg_kinds[0] in (ARG_POS, ARG_OPT) + and arg_type.args + and self_type.type.fullname != "functools._SingleDispatchCallable" + ): + if self_type.type is not arg_type.type: + # We can't map to supertype, since it could trigger expensive checks for + # protocol types, so we consevatively assume this is fine. + return True + + # Fast path: no explicit annotation on self + if all( + ( + type(arg) is TypeVarType + and type(arg.upper_bound) is Instance + and arg.upper_bound.type.fullname == "builtins.object" + ) + for arg in arg_type.args + ): + return True + + from mypy.meet import is_overlapping_types + + return is_overlapping_types(self_type, c.arg_types[0]) + return True + + def erase_to_bound(t: Type) -> Type: # TODO: use value restrictions to produce a union? t = get_proper_type(t) @@ -438,6 +499,7 @@ def make_simplified_union( *, keep_erased: bool = False, contract_literals: bool = True, + handle_recursive: bool = True, ) -> ProperType: """Build union type with redundant union items removed. @@ -463,7 +525,7 @@ def make_simplified_union( to_union(). """ # Step 1: expand all nested unions - items = flatten_nested_unions(items) + items = flatten_nested_unions(items, handle_recursive=handle_recursive) # Step 2: fast path for single item if len(items) == 1: @@ -585,8 +647,11 @@ def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[ return items -def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None: - t = get_proper_type(t) +def _get_type_method_ret_type(t: ProperType, *, name: str) -> Type | None: + # For Enum literals the ret_type can change based on the Enum + # we need to check the type of the enum rather than the literal + if isinstance(t, LiteralType) and t.is_enum_literal(): + t = t.fallback if isinstance(t, Instance): sym = t.type.get(name) @@ -650,6 +715,10 @@ def false_only(t: Type) -> ProperType: new_items = [false_only(item) for item in t.items] can_be_false_items = [item for item in new_items if item.can_be_false] return make_simplified_union(can_be_false_items, line=t.line, column=t.column) + elif isinstance(t, Instance) and t.type.fullname in ("builtins.str", "builtins.bytes"): + return LiteralType("", fallback=t) + elif isinstance(t, Instance) and t.type.fullname == "builtins.int": + return LiteralType(0, fallback=t) else: ret_type = _get_type_method_ret_type(t, name="__bool__") or _get_type_method_ret_type( t, name="__len__" @@ -658,7 +727,10 @@ def false_only(t: Type) -> ProperType: if ret_type: if not ret_type.can_be_false: return UninhabitedType(line=t.line) - elif isinstance(t, Instance) and t.type.is_final: + elif isinstance(t, Instance): + if t.type.is_final or t.type.is_enum: + return UninhabitedType(line=t.line) + elif isinstance(t, LiteralType) and t.is_enum_literal(): return UninhabitedType(line=t.line) new_t = copy_type(t) @@ -892,24 +964,17 @@ class Status(Enum): try_expanding_sum_type_to_union(item, target_fullname) for item in typ.relevant_items() ] return make_simplified_union(items, contract_literals=False) - elif isinstance(typ, Instance) and typ.type.fullname == target_fullname: + + if isinstance(typ, Instance) and typ.type.fullname == target_fullname: + if typ.type.fullname == "builtins.bool": + items = [LiteralType(True, typ), LiteralType(False, typ)] + return make_simplified_union(items, contract_literals=False) + if typ.type.is_enum: - new_items = [] - for name, symbol in typ.type.names.items(): - if not isinstance(symbol.node, Var): - continue - # Skip these since Enum will remove it - if name in ENUM_REMOVED_PROPS: - continue - # Skip private attributes - if name.startswith("__"): - continue - new_items.append(LiteralType(name, typ)) - return make_simplified_union(new_items, contract_literals=False) - elif typ.type.fullname == "builtins.bool": - return make_simplified_union( - [LiteralType(True, typ), LiteralType(False, typ)], contract_literals=False - ) + items = [LiteralType(name, typ) for name in typ.type.enum_members] + if not items: + return typ + return make_simplified_union(items, contract_literals=False) return typ @@ -935,7 +1000,7 @@ def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType] if fullname not in sum_types: sum_types[fullname] = ( ( - set(typ.fallback.get_enum_values()) + set(typ.fallback.type.enum_members) if typ.fallback.type.is_enum else {True, False} ), @@ -968,7 +1033,7 @@ def coerce_to_literal(typ: Type) -> Type: if typ.last_known_value: return typ.last_known_value elif typ.type.is_enum: - enum_values = typ.get_enum_values() + enum_values = typ.type.enum_members if len(enum_values) == 1: return LiteralType(value=enum_values[0], fallback=typ) return original_type diff --git a/mypy/types.py b/mypy/types.py index 52f8a8d63f09e..e92ab0889991e 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -150,10 +150,6 @@ OVERLOAD_NAMES: Final = ("typing.overload", "typing_extensions.overload") -# Attributes that can optionally be defined in the body of a subclass of -# enum.Enum but are removed from the class __dict__ by EnumMeta. -ENUM_REMOVED_PROPS: Final = ("_ignore_", "_order_", "__order__") - NEVER_NAMES: Final = ( "typing.NoReturn", "typing_extensions.NoReturn", @@ -271,9 +267,6 @@ def can_be_true_default(self) -> bool: def can_be_false_default(self) -> bool: return True - def resolve_string_annotation(self) -> Type: - return self - def accept(self, visitor: TypeVisitor[T]) -> T: raise RuntimeError("Not implemented", type(self)) @@ -360,7 +353,7 @@ def _expand_once(self) -> Type: def _partial_expansion(self, nothing_args: bool = False) -> tuple[ProperType, bool]: # Private method mostly for debugging and testing. - unroller = UnrollAliasVisitor(set()) + unroller = UnrollAliasVisitor(set(), {}) if nothing_args: alias = self.copy_modified(args=[UninhabitedType()] * len(self.args)) else: @@ -479,6 +472,20 @@ def accept(self, visitor: TypeVisitor[T]) -> T: return self.item.accept(visitor) +class ReadOnlyType(Type): + """ReadOnly[T] Only usable at top-level of a TypedDict definition.""" + + def __init__(self, item: Type) -> None: + super().__init__(line=item.line, column=item.column) + self.item = item + + def __repr__(self) -> str: + return f"ReadOnly[{self.item}]" + + def accept(self, visitor: TypeVisitor[T]) -> T: + return self.item.accept(visitor) + + class ProperType(Type): """Not a type alias. @@ -906,27 +913,50 @@ def copy_modified( class UnboundType(ProperType): """Instance type that has not been bound during semantic analysis.""" - __slots__ = ("name", "args", "optional", "empty_tuple_index") + __slots__ = ( + "name", + "args", + "optional", + "empty_tuple_index", + "original_str_expr", + "original_str_fallback", + ) def __init__( self, - name: str | None, + name: str, args: Sequence[Type] | None = None, line: int = -1, column: int = -1, optional: bool = False, empty_tuple_index: bool = False, + original_str_expr: str | None = None, + original_str_fallback: str | None = None, ) -> None: super().__init__(line, column) if not args: args = [] - assert name is not None self.name = name self.args = tuple(args) # Should this type be wrapped in an Optional? self.optional = optional # Special case for X[()] self.empty_tuple_index = empty_tuple_index + # If this UnboundType was originally defined as a str or bytes, keep track of + # the original contents of that string-like thing. This way, if this UnboundExpr + # ever shows up inside of a LiteralType, we can determine whether that + # Literal[...] is valid or not. E.g. Literal[foo] is most likely invalid + # (unless 'foo' is an alias for another literal or something) and + # Literal["foo"] most likely is. + # + # We keep track of the entire string instead of just using a boolean flag + # so we can distinguish between things like Literal["foo"] vs + # Literal[" foo "]. + # + # We also keep track of what the original base fallback type was supposed to be + # so we don't have to try and recompute it later + self.original_str_expr = original_str_expr + self.original_str_fallback = original_str_fallback def copy_modified(self, args: Bogus[Sequence[Type] | None] = _dummy) -> UnboundType: if args is _dummy: @@ -938,19 +968,25 @@ def copy_modified(self, args: Bogus[Sequence[Type] | None] = _dummy) -> UnboundT column=self.column, optional=self.optional, empty_tuple_index=self.empty_tuple_index, + original_str_expr=self.original_str_expr, + original_str_fallback=self.original_str_fallback, ) def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_unbound_type(self) def __hash__(self) -> int: - return hash((self.name, self.optional, tuple(self.args))) + return hash((self.name, self.optional, tuple(self.args), self.original_str_expr)) def __eq__(self, other: object) -> bool: if not isinstance(other, UnboundType): return NotImplemented return ( - self.name == other.name and self.optional == other.optional and self.args == other.args + self.name == other.name + and self.optional == other.optional + and self.args == other.args + and self.original_str_expr == other.original_str_expr + and self.original_str_fallback == other.original_str_fallback ) def serialize(self) -> JsonDict: @@ -958,12 +994,19 @@ def serialize(self) -> JsonDict: ".class": "UnboundType", "name": self.name, "args": [a.serialize() for a in self.args], + "expr": self.original_str_expr, + "expr_fallback": self.original_str_fallback, } @classmethod def deserialize(cls, data: JsonDict) -> UnboundType: assert data[".class"] == "UnboundType" - return UnboundType(data["name"], [deserialize_type(a) for a in data["args"]]) + return UnboundType( + data["name"], + [deserialize_type(a) for a in data["args"]], + original_str_expr=data["expr"], + original_str_fallback=data["expr_fallback"], + ) class CallableArgument(ProperType): @@ -1120,15 +1163,18 @@ def copy_modified( # Mark with Bogus because _dummy is just an object (with type Any) type_of_any: int = _dummy_int, original_any: Bogus[AnyType | None] = _dummy, + missing_import_name: Bogus[str | None] = _dummy, ) -> AnyType: if type_of_any == _dummy_int: type_of_any = self.type_of_any if original_any is _dummy: original_any = self.source_any + if missing_import_name is _dummy: + missing_import_name = self.missing_import_name return AnyType( type_of_any=type_of_any, source_any=original_any, - missing_import_name=self.missing_import_name, + missing_import_name=missing_import_name, line=self.line, column=self.column, ) @@ -1417,8 +1463,7 @@ def __init__( self._hash = -1 # Additional attributes defined per instance of this type. For example modules - # have different attributes per instance of types.ModuleType. This is intended - # to be "short-lived", we don't serialize it, and even don't store as variable type. + # have different attributes per instance of types.ModuleType. self.extra_attrs = extra_attrs def accept(self, visitor: TypeVisitor[T]) -> T: @@ -1444,9 +1489,11 @@ def serialize(self) -> JsonDict | str: type_ref = self.type.fullname if not self.args and not self.last_known_value: return type_ref - data: JsonDict = {".class": "Instance"} - data["type_ref"] = type_ref - data["args"] = [arg.serialize() for arg in self.args] + data: JsonDict = { + ".class": "Instance", + "type_ref": type_ref, + "args": [arg.serialize() for arg in self.args], + } if self.last_known_value is not None: data["last_known_value"] = self.last_known_value.serialize() data["extra_attrs"] = self.extra_attrs.serialize() if self.extra_attrs else None @@ -1508,16 +1555,10 @@ def is_singleton_type(self) -> bool: # Also make this return True if the type corresponds to NotImplemented? return ( self.type.is_enum - and len(self.get_enum_values()) == 1 + and len(self.type.enum_members) == 1 or self.type.fullname in {"builtins.ellipsis", "types.EllipsisType"} ) - def get_enum_values(self) -> list[str]: - """Return the list of values for an Enum.""" - return [ - name for name, sym in self.type.names.items() if isinstance(sym.node, mypy.nodes.Var) - ] - class FunctionLike(ProperType): """Abstract base class for function types.""" @@ -1776,8 +1817,8 @@ class CallableType(FunctionLike): "implicit", # Was this type implicitly generated instead of explicitly # specified by the user? "special_sig", # Non-None for signatures that require special handling - # (currently only value is 'dict' for a signature similar to - # 'dict') + # (currently only values are 'dict' for a signature similar to + # 'dict' and 'partial' for a `functools.partial` evaluation) "from_type_type", # Was this callable generated by analyzing Type[...] # instantiation? "bound_args", # Bound type args, mostly unused but may be useful for @@ -2451,6 +2492,9 @@ def slice( if fallback is None: fallback = self.partial_fallback + if stride == 0: + return None + if any(isinstance(t, UnpackType) for t in self.items): total = len(self.items) unpack_index = find_unpack_in_list(self.items) @@ -2519,16 +2563,28 @@ class TypedDictType(ProperType): TODO: The fallback structure is perhaps overly complicated. """ - __slots__ = ("items", "required_keys", "fallback") + __slots__ = ( + "items", + "required_keys", + "readonly_keys", + "fallback", + "extra_items_from", + "to_be_mutated", + ) items: dict[str, Type] # item_name -> item_type required_keys: set[str] + readonly_keys: set[str] fallback: Instance + extra_items_from: list[ProperType] # only used during semantic analysis + to_be_mutated: bool # only used in a plugin for `.update`, `|=`, etc + def __init__( self, items: dict[str, Type], required_keys: set[str], + readonly_keys: set[str], fallback: Instance, line: int = -1, column: int = -1, @@ -2536,20 +2592,31 @@ def __init__( super().__init__(line, column) self.items = items self.required_keys = required_keys + self.readonly_keys = readonly_keys self.fallback = fallback self.can_be_true = len(self.items) > 0 self.can_be_false = len(self.required_keys) == 0 + self.extra_items_from = [] + self.to_be_mutated = False def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_typeddict_type(self) def __hash__(self) -> int: - return hash((frozenset(self.items.items()), self.fallback, frozenset(self.required_keys))) + return hash( + ( + frozenset(self.items.items()), + self.fallback, + frozenset(self.required_keys), + frozenset(self.readonly_keys), + ) + ) def __eq__(self, other: object) -> bool: if not isinstance(other, TypedDictType): return NotImplemented - + if self is other: + return True return ( frozenset(self.items.keys()) == frozenset(other.items.keys()) and all( @@ -2558,6 +2625,7 @@ def __eq__(self, other: object) -> bool: ) and self.fallback == other.fallback and self.required_keys == other.required_keys + and self.readonly_keys == other.readonly_keys ) def serialize(self) -> JsonDict: @@ -2565,6 +2633,7 @@ def serialize(self) -> JsonDict: ".class": "TypedDictType", "items": [[n, t.serialize()] for (n, t) in self.items.items()], "required_keys": sorted(self.required_keys), + "readonly_keys": sorted(self.readonly_keys), "fallback": self.fallback.serialize(), } @@ -2574,6 +2643,7 @@ def deserialize(cls, data: JsonDict) -> TypedDictType: return TypedDictType( {n: deserialize_type(t) for (n, t) in data["items"]}, set(data["required_keys"]), + set(data["readonly_keys"]), Instance.deserialize(data["fallback"]), ) @@ -2597,6 +2667,7 @@ def copy_modified( item_types: list[Type] | None = None, item_names: list[str] | None = None, required_keys: set[str] | None = None, + readonly_keys: set[str] | None = None, ) -> TypedDictType: if fallback is None: fallback = self.fallback @@ -2606,10 +2677,12 @@ def copy_modified( items = dict(zip(self.items, item_types)) if required_keys is None: required_keys = self.required_keys + if readonly_keys is None: + readonly_keys = self.readonly_keys if item_names is not None: items = {k: v for (k, v) in items.items() if k in item_names} required_keys &= set(item_names) - return TypedDictType(items, required_keys, fallback, self.line, self.column) + return TypedDictType(items, required_keys, readonly_keys, fallback, self.line, self.column) def create_anonymous_fallback(self) -> Instance: anonymous = self.as_anonymous() @@ -2642,7 +2715,7 @@ class RawExpressionType(ProperType): This synthetic type is only used at the beginning stages of semantic analysis and should be completely removing during the process for mapping UnboundTypes to - actual types: we turn it into its "node" argument, a LiteralType, or an AnyType. + actual types: we either turn it into a LiteralType or an AnyType. For example, suppose `Foo[1]` is initially represented as the following: @@ -2680,7 +2753,7 @@ class RawExpressionType(ProperType): ) """ - __slots__ = ("literal_value", "base_type_name", "note", "node") + __slots__ = ("literal_value", "base_type_name", "note") def __init__( self, @@ -2689,13 +2762,11 @@ def __init__( line: int = -1, column: int = -1, note: str | None = None, - node: Type | None = None, ) -> None: super().__init__(line, column) self.literal_value = literal_value self.base_type_name = base_type_name self.note = note - self.node = node def simple_name(self) -> str: return self.base_type_name.replace("builtins.", "") @@ -2705,21 +2776,6 @@ def accept(self, visitor: TypeVisitor[T]) -> T: ret: T = visitor.visit_raw_expression_type(self) return ret - def copy_modified(self, node: Type | None) -> RawExpressionType: - return RawExpressionType( - literal_value=self.literal_value, - base_type_name=self.base_type_name, - line=self.line, - column=self.column, - note=self.note, - node=node, - ) - - def resolve_string_annotation(self) -> Type: - if self.node is not None: - return self.node.resolve_string_annotation() - return self - def serialize(self) -> JsonDict: assert False, "Synthetic types don't serialize" @@ -2731,7 +2787,6 @@ def __eq__(self, other: object) -> bool: return ( self.base_type_name == other.base_type_name and self.literal_value == other.literal_value - and self.node == other.node ) else: return NotImplemented @@ -2763,10 +2818,28 @@ def __init__( self.fallback = fallback self._hash = -1 # Cached hash value + # NOTE: Enum types are always truthy by default, but this can be changed + # in subclasses, so we need to get the truthyness from the Enum + # type rather than base it on the value (which is a non-empty + # string for enums, so always truthy) + # TODO: We should consider moving this branch to the `can_be_true` + # `can_be_false` properties instead, so the truthyness only + # needs to be determined once per set of Enum literals. + # However, the same can be said for `TypeAliasType` in some + # cases and we only set the default based on the type it is + # aliasing. So if we decide to change this, we may want to + # change that as well. perf_compare output was inconclusive + # but slightly favored this version, probably because we have + # almost no test cases where we would redundantly compute + # `can_be_false`/`can_be_true`. def can_be_false_default(self) -> bool: + if self.fallback.type.is_enum: + return self.fallback.can_be_false return not self.value def can_be_true_default(self) -> bool: + if self.fallback.type.is_enum: + return self.fallback.can_be_true return bool(self.value) def accept(self, visitor: TypeVisitor[T]) -> T: @@ -2829,7 +2902,13 @@ def is_singleton_type(self) -> bool: class UnionType(ProperType): """The union type Union[T1, ..., Tn] (at least one type argument).""" - __slots__ = ("items", "is_evaluated", "uses_pep604_syntax") + __slots__ = ( + "items", + "is_evaluated", + "uses_pep604_syntax", + "original_str_expr", + "original_str_fallback", + ) def __init__( self, @@ -2848,6 +2927,11 @@ def __init__( self.is_evaluated = is_evaluated # uses_pep604_syntax is True if Union uses OR syntax (X | Y) self.uses_pep604_syntax = uses_pep604_syntax + # The meaning of these two is the same as for UnboundType. A UnionType can be + # return by type parser from a string "A|B", and we need to be able to fall back + # to plain string, when such a string appears inside a Literal[...]. + self.original_str_expr: str | None = None + self.original_str_fallback: str | None = None def can_be_true_default(self) -> bool: return any(item.can_be_true for item in self.items) @@ -2896,12 +2980,19 @@ def relevant_items(self) -> list[Type]: return [i for i in self.items if not isinstance(get_proper_type(i), NoneType)] def serialize(self) -> JsonDict: - return {".class": "UnionType", "items": [t.serialize() for t in self.items]} + return { + ".class": "UnionType", + "items": [t.serialize() for t in self.items], + "uses_pep604_syntax": self.uses_pep604_syntax, + } @classmethod def deserialize(cls, data: JsonDict) -> UnionType: assert data[".class"] == "UnionType" - return UnionType([deserialize_type(t) for t in data["items"]]) + return UnionType( + [deserialize_type(t) for t in data["items"]], + uses_pep604_syntax=data["uses_pep604_syntax"], + ) class PartialType(ProperType): @@ -3383,10 +3474,12 @@ def visit_tuple_type(self, t: TupleType) -> str: def visit_typeddict_type(self, t: TypedDictType) -> str: def item_str(name: str, typ: str) -> str: - if name in t.required_keys: - return f"{name!r}: {typ}" - else: - return f"{name!r}?: {typ}" + modifier = "" + if name not in t.required_keys: + modifier += "?" + if name in t.readonly_keys: + modifier += "=" + return f"{name!r}{modifier}: {typ}" s = ( "{" @@ -3400,8 +3493,6 @@ def item_str(name: str, typ: str) -> str: return f"TypedDict({prefix}{s})" def visit_raw_expression_type(self, t: RawExpressionType) -> str: - if t.node is not None: - return t.node.accept(self) return repr(t.literal_value) def visit_literal_type(self, t: LiteralType) -> str: @@ -3465,9 +3556,6 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type: return t def visit_raw_expression_type(self, t: RawExpressionType) -> Type: - if t.node is not None: - node = t.node.accept(self) - return t.copy_modified(node=node) return t def visit_type_list(self, t: TypeList) -> Type: @@ -3475,7 +3563,11 @@ def visit_type_list(self, t: TypeList) -> Type: class UnrollAliasVisitor(TrivialSyntheticTypeTranslator): - def __init__(self, initial_aliases: set[TypeAliasType]) -> None: + def __init__( + self, initial_aliases: set[TypeAliasType], cache: dict[Type, Type] | None + ) -> None: + assert cache is not None + super().__init__(cache) self.recursed = False self.initial_aliases = initial_aliases @@ -3487,7 +3579,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: # A = Tuple[B, B] # B = int # will not be detected as recursive on the second encounter of B. - subvisitor = UnrollAliasVisitor(self.initial_aliases | {t}) + subvisitor = UnrollAliasVisitor(self.initial_aliases | {t}, self.cache) result = get_proper_type(t).accept(subvisitor) if subvisitor.recursed: self.recursed = True @@ -3520,6 +3612,8 @@ def visit_type_alias_type(self, typ: TypeAliasType) -> None: class HasTypeVars(BoolTypeQuery): + """Visitor for querying whether a type has a type variable component.""" + def __init__(self) -> None: super().__init__(ANY_STRATEGY) self.skip_alias_target = True @@ -3597,7 +3691,7 @@ def extend_args_for_prefix_and_suffix( def flatten_nested_unions( - types: Sequence[Type], handle_type_alias_type: bool = True + types: Sequence[Type], *, handle_type_alias_type: bool = True, handle_recursive: bool = True ) -> list[Type]: """Flatten nested unions in a type list.""" if not isinstance(types, list): @@ -3611,7 +3705,13 @@ def flatten_nested_unions( flat_items: list[Type] = [] for t in typelist: - tp = get_proper_type(t) if handle_type_alias_type else t + if handle_type_alias_type: + if not handle_recursive and isinstance(t, TypeAliasType) and t.is_recursive: + tp: Type = t + else: + tp = get_proper_type(t) + else: + tp = t if isinstance(tp, ProperType) and isinstance(tp, UnionType): flat_items.extend( flatten_nested_unions(tp.items, handle_type_alias_type=handle_type_alias_type) diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index 7b9ce2864484d..7ff14c55d3a8b 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -20,34 +20,54 @@ __future__: 3.0- __main__: 3.0- _ast: 3.0- +_asyncio: 3.0- _bisect: 3.0- +_blake2: 3.6- _bootlocale: 3.4-3.9 +_bz2: 3.3- _codecs: 3.0- _collections_abc: 3.3- _compat_pickle: 3.1- _compression: 3.5- +_contextvars: 3.7- _csv: 3.0- _ctypes: 3.0- _curses: 3.0- +_curses_panel: 3.0- +_dbm: 3.0- _decimal: 3.3- _dummy_thread: 3.0-3.8 _dummy_threading: 3.0-3.8 +_frozen_importlib: 3.0- +_frozen_importlib_external: 3.5- +_gdbm: 3.0- +_hashlib: 3.0- _heapq: 3.0- _imp: 3.0- +_interpchannels: 3.13- +_interpqueues: 3.13- +_interpreters: 3.13- +_io: 3.0- _json: 3.0- _locale: 3.0- _lsprof: 3.0- +_lzma: 3.3- _markupbase: 3.0- -_msi: 3.0- +_msi: 3.0-3.12 +_multibytecodec: 3.0- _operator: 3.4- _osx_support: 3.0- _posixsubprocess: 3.2- _py_abc: 3.7- _pydecimal: 3.5- +_queue: 3.7- _random: 3.0- _sitebuiltins: 3.4- _socket: 3.0- # present in 3.0 at runtime, but not in typeshed +_sqlite3: 3.0- +_ssl: 3.0- _stat: 3.4- +_struct: 3.0- _thread: 3.0- _threading_local: 3.0- _tkinter: 3.0- @@ -111,6 +131,7 @@ curses: 3.0- dataclasses: 3.7- datetime: 3.0- dbm: 3.0- +dbm.sqlite3: 3.13- decimal: 3.0- difflib: 3.0- dis: 3.0- @@ -121,6 +142,12 @@ doctest: 3.0- dummy_threading: 3.0-3.8 email: 3.0- encodings: 3.0- +encodings.cp1125: 3.4- +encodings.cp273: 3.4- +encodings.cp858: 3.2- +encodings.koi8_t: 3.5- +encodings.kz1048: 3.5- +encodings.mac_centeuro: 3.0-3.8 ensurepip: 3.0- enum: 3.4- errno: 3.0- @@ -152,10 +179,15 @@ imghdr: 3.0-3.12 imp: 3.0-3.11 importlib: 3.0- importlib._abc: 3.10- +importlib._bootstrap: 3.0- +importlib._bootstrap_external: 3.5- importlib.metadata: 3.8- importlib.metadata._meta: 3.10- +importlib.metadata.diagnose: 3.13- importlib.readers: 3.10- importlib.resources: 3.7- +importlib.resources._common: 3.11- +importlib.resources._functional: 3.13- importlib.resources.abc: 3.11- importlib.resources.readers: 3.11- importlib.resources.simple: 3.11- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index d14c6d39a162e..8dc1bcbea32c5 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,1233 +1,149 @@ import sys -import typing_extensions -from typing import Any, ClassVar, Generic, Literal, TypedDict, overload -from typing_extensions import Unpack +from ast import ( + AST as AST, + Add as Add, + And as And, + AnnAssign as AnnAssign, + Assert as Assert, + Assign as Assign, + AsyncFor as AsyncFor, + AsyncFunctionDef as AsyncFunctionDef, + AsyncWith as AsyncWith, + Attribute as Attribute, + AugAssign as AugAssign, + Await as Await, + BinOp as BinOp, + BitAnd as BitAnd, + BitOr as BitOr, + BitXor as BitXor, + BoolOp as BoolOp, + Break as Break, + Call as Call, + ClassDef as ClassDef, + Compare as Compare, + Constant as Constant, + Continue as Continue, + Del as Del, + Delete as Delete, + Dict as Dict, + DictComp as DictComp, + Div as Div, + Eq as Eq, + ExceptHandler as ExceptHandler, + Expr as Expr, + Expression as Expression, + FloorDiv as FloorDiv, + For as For, + FormattedValue as FormattedValue, + FunctionDef as FunctionDef, + FunctionType as FunctionType, + GeneratorExp as GeneratorExp, + Global as Global, + Gt as Gt, + GtE as GtE, + If as If, + IfExp as IfExp, + Import as Import, + ImportFrom as ImportFrom, + In as In, + Interactive as Interactive, + Invert as Invert, + Is as Is, + IsNot as IsNot, + JoinedStr as JoinedStr, + Lambda as Lambda, + List as List, + ListComp as ListComp, + Load as Load, + LShift as LShift, + Lt as Lt, + LtE as LtE, + MatMult as MatMult, + Mod as Mod, + Module as Module, + Mult as Mult, + Name as Name, + NamedExpr as NamedExpr, + Nonlocal as Nonlocal, + Not as Not, + NotEq as NotEq, + NotIn as NotIn, + Or as Or, + Pass as Pass, + Pow as Pow, + Raise as Raise, + Return as Return, + RShift as RShift, + Set as Set, + SetComp as SetComp, + Slice as Slice, + Starred as Starred, + Store as Store, + Sub as Sub, + Subscript as Subscript, + Try as Try, + Tuple as Tuple, + TypeIgnore as TypeIgnore, + UAdd as UAdd, + UnaryOp as UnaryOp, + USub as USub, + While as While, + With as With, + Yield as Yield, + YieldFrom as YieldFrom, + alias as alias, + arg as arg, + arguments as arguments, + boolop as boolop, + cmpop as cmpop, + comprehension as comprehension, + excepthandler as excepthandler, + expr as expr, + expr_context as expr_context, + keyword as keyword, + mod as mod, + operator as operator, + stmt as stmt, + type_ignore as type_ignore, + unaryop as unaryop, + withitem as withitem, +) +from typing import Literal -PyCF_ONLY_AST: Literal[1024] -PyCF_TYPE_COMMENTS: Literal[4096] -PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] - -if sys.version_info >= (3, 13): - PyCF_OPTIMIZED_AST: Literal[33792] - -# Used for node end positions in constructor keyword arguments -_EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None) - -# Alias used for fields that must always be valid identifiers -# A string `x` counts as a valid identifier if both the following are True -# (1) `x.isidentifier()` evaluates to `True` -# (2) `keyword.iskeyword(x)` evaluates to `False` -_Identifier: typing_extensions.TypeAlias = str - -# Corresponds to the names in the `_attributes` class variable which is non-empty in certain AST nodes -class _Attributes(TypedDict, Generic[_EndPositionT], total=False): - lineno: int - col_offset: int - end_lineno: _EndPositionT - end_col_offset: _EndPositionT - -class AST: - if sys.version_info >= (3, 10): - __match_args__ = () - _attributes: ClassVar[tuple[str, ...]] - _fields: ClassVar[tuple[str, ...]] - if sys.version_info >= (3, 13): - _field_types: ClassVar[dict[str, Any]] - -class mod(AST): ... -class type_ignore(AST): ... - -class TypeIgnore(type_ignore): - if sys.version_info >= (3, 10): - __match_args__ = ("lineno", "tag") - lineno: int - tag: str - def __init__(self, lineno: int, tag: str) -> None: ... - -class FunctionType(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("argtypes", "returns") - argtypes: list[expr] - returns: expr - if sys.version_info >= (3, 13): - @overload - def __init__(self, argtypes: list[expr], returns: expr) -> None: ... - @overload - def __init__(self, argtypes: list[expr] = ..., *, returns: expr) -> None: ... - else: - def __init__(self, argtypes: list[expr], returns: expr) -> None: ... - -class Module(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("body", "type_ignores") - body: list[stmt] - type_ignores: list[TypeIgnore] - if sys.version_info >= (3, 13): - def __init__(self, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> None: ... - else: - def __init__(self, body: list[stmt], type_ignores: list[TypeIgnore]) -> None: ... - -class Interactive(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("body",) - body: list[stmt] - if sys.version_info >= (3, 13): - def __init__(self, body: list[stmt] = ...) -> None: ... - else: - def __init__(self, body: list[stmt]) -> None: ... - -class Expression(mod): - if sys.version_info >= (3, 10): - __match_args__ = ("body",) - body: expr - def __init__(self, body: expr) -> None: ... - -class stmt(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... - -class FunctionDef(stmt): - if sys.version_info >= (3, 12): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") - elif sys.version_info >= (3, 10): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") - name: _Identifier - args: arguments - body: list[stmt] - decorator_list: list[expr] - returns: expr | None - type_comment: str | None - if sys.version_info >= (3, 12): - type_params: list[type_param] - if sys.version_info >= (3, 13): - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt] = ..., - decorator_list: list[expr] = ..., - returns: expr | None = None, - type_comment: str | None = None, - type_params: list[type_param] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - elif sys.version_info >= (3, 12): - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None, - type_comment: str | None, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - *, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - -class AsyncFunctionDef(stmt): - if sys.version_info >= (3, 12): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") - elif sys.version_info >= (3, 10): - __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") - name: _Identifier - args: arguments - body: list[stmt] - decorator_list: list[expr] - returns: expr | None - type_comment: str | None - if sys.version_info >= (3, 12): - type_params: list[type_param] - if sys.version_info >= (3, 13): - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt] = ..., - decorator_list: list[expr] = ..., - returns: expr | None = None, - type_comment: str | None = None, - type_params: list[type_param] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - elif sys.version_info >= (3, 12): - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None, - type_comment: str | None, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - @overload - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - *, - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - name: _Identifier, - args: arguments, - body: list[stmt], - decorator_list: list[expr], - returns: expr | None = None, - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - -class ClassDef(stmt): - if sys.version_info >= (3, 12): - __match_args__ = ("name", "bases", "keywords", "body", "decorator_list", "type_params") - elif sys.version_info >= (3, 10): - __match_args__ = ("name", "bases", "keywords", "body", "decorator_list") - name: _Identifier - bases: list[expr] - keywords: list[keyword] - body: list[stmt] - decorator_list: list[expr] - if sys.version_info >= (3, 12): - type_params: list[type_param] - if sys.version_info >= (3, 13): - def __init__( - self, - name: _Identifier, - bases: list[expr] = ..., - keywords: list[keyword] = ..., - body: list[stmt] = ..., - decorator_list: list[expr] = ..., - type_params: list[type_param] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - elif sys.version_info >= (3, 12): - def __init__( - self, - name: _Identifier, - bases: list[expr], - keywords: list[keyword], - body: list[stmt], - decorator_list: list[expr], - type_params: list[type_param], - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - name: _Identifier, - bases: list[expr], - keywords: list[keyword], - body: list[stmt], - decorator_list: list[expr], - **kwargs: Unpack[_Attributes], - ) -> None: ... - -class Return(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr | None - def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - -class Delete(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("targets",) - targets: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, targets: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - -class Assign(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("targets", "value", "type_comment") - targets: list[expr] - value: expr - type_comment: str | None - if sys.version_info >= (3, 13): - @overload - def __init__( - self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - @overload - def __init__( - self, targets: list[expr] = ..., *, value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__( - self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - -class AugAssign(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "op", "value") - target: Name | Attribute | Subscript - op: operator - value: expr - def __init__( - self, target: Name | Attribute | Subscript, op: operator, value: expr, **kwargs: Unpack[_Attributes] - ) -> None: ... - -class AnnAssign(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "annotation", "value", "simple") - target: Name | Attribute | Subscript - annotation: expr - value: expr | None - simple: int - @overload - def __init__( - self, - target: Name | Attribute | Subscript, - annotation: expr, - value: expr | None, - simple: int, - **kwargs: Unpack[_Attributes], - ) -> None: ... - @overload - def __init__( - self, - target: Name | Attribute | Subscript, - annotation: expr, - value: expr | None = None, - *, - simple: int, - **kwargs: Unpack[_Attributes], - ) -> None: ... - -class For(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "iter", "body", "orelse", "type_comment") - target: expr - iter: expr - body: list[stmt] - orelse: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt] = ..., - orelse: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt], - orelse: list[stmt], - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - -class AsyncFor(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "iter", "body", "orelse", "type_comment") - target: expr - iter: expr - body: list[stmt] - orelse: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt] = ..., - orelse: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - target: expr, - iter: expr, - body: list[stmt], - orelse: list[stmt], - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - -class While(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "body", "orelse") - test: expr - body: list[stmt] - orelse: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... - -class If(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "body", "orelse") - test: expr - body: list[stmt] - orelse: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... - -class With(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("items", "body", "type_comment") - items: list[withitem] - body: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - items: list[withitem] = ..., - body: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - -class AsyncWith(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("items", "body", "type_comment") - items: list[withitem] - body: list[stmt] - type_comment: str | None - if sys.version_info >= (3, 13): - def __init__( - self, - items: list[withitem] = ..., - body: list[stmt] = ..., - type_comment: str | None = None, - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - -class Raise(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("exc", "cause") - exc: expr | None - cause: expr | None - def __init__(self, exc: expr | None = None, cause: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - -class Try(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("body", "handlers", "orelse", "finalbody") - body: list[stmt] - handlers: list[ExceptHandler] - orelse: list[stmt] - finalbody: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, - body: list[stmt] = ..., - handlers: list[ExceptHandler] = ..., - orelse: list[stmt] = ..., - finalbody: list[stmt] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - body: list[stmt], - handlers: list[ExceptHandler], - orelse: list[stmt], - finalbody: list[stmt], - **kwargs: Unpack[_Attributes], - ) -> None: ... +if sys.version_info >= (3, 12): + from ast import ParamSpec as ParamSpec, TypeVar as TypeVar, TypeVarTuple as TypeVarTuple, type_param as type_param if sys.version_info >= (3, 11): - class TryStar(stmt): - __match_args__ = ("body", "handlers", "orelse", "finalbody") - body: list[stmt] - handlers: list[ExceptHandler] - orelse: list[stmt] - finalbody: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, - body: list[stmt] = ..., - handlers: list[ExceptHandler] = ..., - orelse: list[stmt] = ..., - finalbody: list[stmt] = ..., - **kwargs: Unpack[_Attributes], - ) -> None: ... - else: - def __init__( - self, - body: list[stmt], - handlers: list[ExceptHandler], - orelse: list[stmt], - finalbody: list[stmt], - **kwargs: Unpack[_Attributes], - ) -> None: ... - -class Assert(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "msg") - test: expr - msg: expr | None - def __init__(self, test: expr, msg: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - -class Import(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("names",) - names: list[alias] - if sys.version_info >= (3, 13): - def __init__(self, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, names: list[alias], **kwargs: Unpack[_Attributes]) -> None: ... - -class ImportFrom(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("module", "names", "level") - module: str | None - names: list[alias] - level: int - if sys.version_info >= (3, 13): - @overload - def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... - @overload - def __init__( - self, module: str | None = None, names: list[alias] = ..., *, level: int, **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - @overload - def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... - @overload - def __init__( - self, module: str | None = None, *, names: list[alias], level: int, **kwargs: Unpack[_Attributes] - ) -> None: ... - -class Global(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("names",) - names: list[_Identifier] - if sys.version_info >= (3, 13): - def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... - -class Nonlocal(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("names",) - names: list[_Identifier] - if sys.version_info >= (3, 13): - def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... - -class Expr(stmt): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class Pass(stmt): ... -class Break(stmt): ... -class Continue(stmt): ... - -class expr(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... - -class BoolOp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("op", "values") - op: boolop - values: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, op: boolop, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, op: boolop, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - -class BinOp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("left", "op", "right") - left: expr - op: operator - right: expr - def __init__(self, left: expr, op: operator, right: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class UnaryOp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("op", "operand") - op: unaryop - operand: expr - def __init__(self, op: unaryop, operand: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class Lambda(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("args", "body") - args: arguments - body: expr - def __init__(self, args: arguments, body: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class IfExp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("test", "body", "orelse") - test: expr - body: expr - orelse: expr - def __init__(self, test: expr, body: expr, orelse: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class Dict(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("keys", "values") - keys: list[expr | None] - values: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, keys: list[expr | None], values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - -class Set(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elts",) - elts: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elts: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - -class ListComp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elt", "generators") - elt: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - -class SetComp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elt", "generators") - elt: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - -class DictComp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("key", "value", "generators") - key: expr - value: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__( - self, key: expr, value: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, key: expr, value: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - -class GeneratorExp(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elt", "generators") - elt: expr - generators: list[comprehension] - if sys.version_info >= (3, 13): - def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... - -class Await(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class Yield(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr | None - def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - -class YieldFrom(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class Compare(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("left", "ops", "comparators") - left: expr - ops: list[cmpop] - comparators: list[expr] - if sys.version_info >= (3, 13): - def __init__( - self, left: expr, ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, left: expr, ops: list[cmpop], comparators: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - -class Call(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("func", "args", "keywords") - func: expr - args: list[expr] - keywords: list[keyword] - if sys.version_info >= (3, 13): - def __init__( - self, func: expr, args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - def __init__(self, func: expr, args: list[expr], keywords: list[keyword], **kwargs: Unpack[_Attributes]) -> None: ... - -class FormattedValue(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "conversion", "format_spec") - value: expr - conversion: int - format_spec: expr | None - def __init__(self, value: expr, conversion: int, format_spec: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - -class JoinedStr(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("values",) - values: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... - -class Constant(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "kind") - value: Any # None, str, bytes, bool, int, float, complex, Ellipsis - kind: str | None - # Aliases for value, for backwards compatibility - s: Any - n: int | float | complex - def __init__(self, value: Any, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - -class NamedExpr(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "value") - target: Name - value: expr - def __init__(self, target: Name, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class Attribute(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "attr", "ctx") - value: expr - attr: _Identifier - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, value: expr, attr: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - -if sys.version_info >= (3, 9): - _Slice: typing_extensions.TypeAlias = expr - _SliceAttributes: typing_extensions.TypeAlias = _Attributes -else: - class slice(AST): ... - _Slice: typing_extensions.TypeAlias = slice - - class _SliceAttributes(TypedDict): ... - -class Slice(_Slice): - if sys.version_info >= (3, 10): - __match_args__ = ("lower", "upper", "step") - lower: expr | None - upper: expr | None - step: expr | None - def __init__( - self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_SliceAttributes] - ) -> None: ... - -if sys.version_info < (3, 9): - class ExtSlice(slice): - dims: list[slice] - def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ... - - class Index(slice): - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ... - -class Subscript(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "slice", "ctx") - value: expr - slice: _Slice - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, value: expr, slice: _Slice, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - -class Starred(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("value", "ctx") - value: expr - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, value: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - -class Name(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("id", "ctx") - id: _Identifier - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - def __init__(self, id: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - -class List(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elts", "ctx") - elts: list[expr] - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - if sys.version_info >= (3, 13): - def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - -class Tuple(expr): - if sys.version_info >= (3, 10): - __match_args__ = ("elts", "ctx") - elts: list[expr] - ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` - if sys.version_info >= (3, 9): - dims: list[expr] - if sys.version_info >= (3, 13): - def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... - -class expr_context(AST): ... - -if sys.version_info < (3, 9): - class AugLoad(expr_context): ... - class AugStore(expr_context): ... - class Param(expr_context): ... - - class Suite(mod): - body: list[stmt] - def __init__(self, body: list[stmt]) -> None: ... - -class Del(expr_context): ... -class Load(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 MatMult(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): - if sys.version_info >= (3, 10): - __match_args__ = ("target", "iter", "ifs", "is_async") - target: expr - iter: expr - ifs: list[expr] - is_async: int - if sys.version_info >= (3, 13): - @overload - def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... - @overload - def __init__(self, target: expr, iter: expr, ifs: list[expr] = ..., *, is_async: int) -> None: ... - else: - def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... - -class excepthandler(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... - -class ExceptHandler(excepthandler): - if sys.version_info >= (3, 10): - __match_args__ = ("type", "name", "body") - type: expr | None - name: _Identifier | None - body: list[stmt] - if sys.version_info >= (3, 13): - def __init__( - self, type: expr | None = None, name: _Identifier | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes] - ) -> None: ... - else: - @overload - def __init__( - self, type: expr | None, name: _Identifier | None, body: list[stmt], **kwargs: Unpack[_Attributes] - ) -> None: ... - @overload - def __init__( - self, type: expr | None = None, name: _Identifier | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes] - ) -> None: ... - -class arguments(AST): - if sys.version_info >= (3, 10): - __match_args__ = ("posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults") - posonlyargs: list[arg] - args: list[arg] - vararg: arg | None - kwonlyargs: list[arg] - kw_defaults: list[expr | None] - kwarg: arg | None - defaults: list[expr] - if sys.version_info >= (3, 13): - def __init__( - self, - posonlyargs: list[arg] = ..., - args: list[arg] = ..., - vararg: arg | None = None, - kwonlyargs: list[arg] = ..., - kw_defaults: list[expr | None] = ..., - kwarg: arg | None = None, - defaults: list[expr] = ..., - ) -> None: ... - else: - @overload - def __init__( - self, - posonlyargs: list[arg], - args: list[arg], - vararg: arg | None, - kwonlyargs: list[arg], - kw_defaults: list[expr | None], - kwarg: arg | None, - defaults: list[expr], - ) -> None: ... - @overload - def __init__( - self, - posonlyargs: list[arg], - args: list[arg], - vararg: arg | None, - kwonlyargs: list[arg], - kw_defaults: list[expr | None], - kwarg: arg | None = None, - *, - defaults: list[expr], - ) -> None: ... - @overload - def __init__( - self, - posonlyargs: list[arg], - args: list[arg], - vararg: arg | None = None, - *, - kwonlyargs: list[arg], - kw_defaults: list[expr | None], - kwarg: arg | None = None, - defaults: list[expr], - ) -> None: ... - -class arg(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - if sys.version_info >= (3, 10): - __match_args__ = ("arg", "annotation", "type_comment") - arg: _Identifier - annotation: expr | None - type_comment: str | None - def __init__( - self, arg: _Identifier, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes] - ) -> None: ... - -class keyword(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - if sys.version_info >= (3, 10): - __match_args__ = ("arg", "value") - arg: _Identifier | None - value: expr - @overload - def __init__(self, arg: _Identifier | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - @overload - def __init__(self, arg: _Identifier | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... - -class alias(AST): - lineno: int - col_offset: int - end_lineno: int | None - end_col_offset: int | None - if sys.version_info >= (3, 10): - __match_args__ = ("name", "asname") - name: str - asname: _Identifier | None - def __init__(self, name: str, asname: _Identifier | None = None, **kwargs: Unpack[_Attributes]) -> None: ... - -class withitem(AST): - if sys.version_info >= (3, 10): - __match_args__ = ("context_expr", "optional_vars") - context_expr: expr - optional_vars: expr | None - def __init__(self, context_expr: expr, optional_vars: expr | None = None) -> None: ... + from ast import TryStar as TryStar if sys.version_info >= (3, 10): - class Match(stmt): - __match_args__ = ("subject", "cases") - subject: expr - cases: list[match_case] - if sys.version_info >= (3, 13): - def __init__(self, subject: expr, cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]) -> None: ... - else: - def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ... - - class pattern(AST): - lineno: int - col_offset: int - end_lineno: int - end_col_offset: int - def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - # Without the alias, Pyright complains variables named pattern are recursively defined - _Pattern: typing_extensions.TypeAlias = pattern - - class match_case(AST): - __match_args__ = ("pattern", "guard", "body") - pattern: _Pattern - guard: expr | None - body: list[stmt] - if sys.version_info >= (3, 13): - def __init__(self, pattern: _Pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ... - else: - @overload - def __init__(self, pattern: _Pattern, guard: expr | None, body: list[stmt]) -> None: ... - @overload - def __init__(self, pattern: _Pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ... - - class MatchValue(pattern): - __match_args__ = ("value",) - value: expr - def __init__(self, value: expr, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - class MatchSingleton(pattern): - __match_args__ = ("value",) - value: Literal[True, False] | None - def __init__(self, value: Literal[True, False] | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - class MatchSequence(pattern): - __match_args__ = ("patterns",) - patterns: list[pattern] - if sys.version_info >= (3, 13): - def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... - else: - def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... - - class MatchStar(pattern): - __match_args__ = ("name",) - name: _Identifier | None - def __init__(self, name: _Identifier | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + from ast import ( + MatchAs as MatchAs, + MatchClass as MatchClass, + MatchMapping as MatchMapping, + MatchOr as MatchOr, + MatchSequence as MatchSequence, + MatchSingleton as MatchSingleton, + MatchStar as MatchStar, + MatchValue as MatchValue, + match_case as match_case, + pattern as pattern, + ) - class MatchMapping(pattern): - __match_args__ = ("keys", "patterns", "rest") - keys: list[expr] - patterns: list[pattern] - rest: _Identifier | None - if sys.version_info >= (3, 13): - def __init__( - self, - keys: list[expr] = ..., - patterns: list[pattern] = ..., - rest: _Identifier | None = None, - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - else: - def __init__( - self, - keys: list[expr], - patterns: list[pattern], - rest: _Identifier | None = None, - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - - class MatchClass(pattern): - __match_args__ = ("cls", "patterns", "kwd_attrs", "kwd_patterns") - cls: expr - patterns: list[pattern] - kwd_attrs: list[_Identifier] - kwd_patterns: list[pattern] - if sys.version_info >= (3, 13): - def __init__( - self, - cls: expr, - patterns: list[pattern] = ..., - kwd_attrs: list[_Identifier] = ..., - kwd_patterns: list[pattern] = ..., - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - else: - def __init__( - self, - cls: expr, - patterns: list[pattern], - kwd_attrs: list[_Identifier], - kwd_patterns: list[pattern], - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - - class MatchAs(pattern): - __match_args__ = ("pattern", "name") - pattern: _Pattern | None - name: _Identifier | None - def __init__( - self, pattern: _Pattern | None = None, name: _Identifier | None = None, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - - class MatchOr(pattern): - __match_args__ = ("patterns",) - patterns: list[pattern] - if sys.version_info >= (3, 13): - def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... - else: - def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... - -if sys.version_info >= (3, 12): - class type_param(AST): - lineno: int - col_offset: int - end_lineno: int - end_col_offset: int - def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - class TypeVar(type_param): - if sys.version_info >= (3, 13): - __match_args__ = ("name", "bound", "default_value") - else: - __match_args__ = ("name", "bound") - name: _Identifier - bound: expr | None - if sys.version_info >= (3, 13): - default_value: expr | None - def __init__( - self, - name: _Identifier, - bound: expr | None = None, - default_value: expr | None = None, - **kwargs: Unpack[_Attributes[int]], - ) -> None: ... - else: - def __init__(self, name: _Identifier, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... - - class ParamSpec(type_param): - if sys.version_info >= (3, 13): - __match_args__ = ("name", "default_value") - else: - __match_args__ = ("name",) - name: _Identifier - if sys.version_info >= (3, 13): - default_value: expr | None - def __init__( - self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - else: - def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... +if sys.version_info < (3, 9): + from ast import ( + AugLoad as AugLoad, + AugStore as AugStore, + ExtSlice as ExtSlice, + Index as Index, + Param as Param, + Suite as Suite, + slice as slice, + ) - class TypeVarTuple(type_param): - if sys.version_info >= (3, 13): - __match_args__ = ("name", "default_value") - else: - __match_args__ = ("name",) - name: _Identifier - if sys.version_info >= (3, 13): - default_value: expr | None - def __init__( - self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - else: - def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... +PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] +PyCF_ONLY_AST: Literal[1024] +PyCF_TYPE_COMMENTS: Literal[4096] - class TypeAlias(stmt): - __match_args__ = ("name", "type_params", "value") - name: Name - type_params: list[type_param] - value: expr - if sys.version_info >= (3, 13): - @overload - def __init__( - self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - @overload - def __init__( - self, name: Name, type_params: list[type_param] = ..., *, value: expr, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... - else: - def __init__( - self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] - ) -> None: ... +if sys.version_info >= (3, 13): + PyCF_OPTIMIZED_AST: Literal[33792] diff --git a/mypy/typeshed/stdlib/_asyncio.pyi b/mypy/typeshed/stdlib/_asyncio.pyi new file mode 100644 index 0000000000000..18920cd8a8a48 --- /dev/null +++ b/mypy/typeshed/stdlib/_asyncio.pyi @@ -0,0 +1,120 @@ +import sys +from asyncio.events import AbstractEventLoop +from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable +from contextvars import Context +from types import FrameType +from typing import Any, Literal, TextIO, TypeVar +from typing_extensions import Self, TypeAlias + +if sys.version_info >= (3, 9): + from types import GenericAlias + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_TaskYieldType: TypeAlias = Future[object] | None + +class Future(Awaitable[_T], Iterable[_T]): + _state: str + @property + def _exception(self) -> BaseException | None: ... + _blocking: bool + @property + def _log_traceback(self) -> bool: ... + @_log_traceback.setter + def _log_traceback(self, val: Literal[False]) -> None: ... + _asyncio_future_blocking: bool # is a part of duck-typing contract for `Future` + def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ... + def __del__(self) -> None: ... + def get_loop(self) -> AbstractEventLoop: ... + @property + def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ... + def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ... + if sys.version_info >= (3, 9): + def cancel(self, msg: Any | None = None) -> bool: ... + else: + def cancel(self) -> bool: ... + + def cancelled(self) -> bool: ... + def done(self) -> bool: ... + def result(self) -> _T: ... + def exception(self) -> BaseException | None: ... + def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ... + def set_result(self, result: _T, /) -> None: ... + def set_exception(self, exception: type | BaseException, /) -> None: ... + def __iter__(self) -> Generator[Any, None, _T]: ... + def __await__(self) -> Generator[Any, None, _T]: ... + @property + def _loop(self) -> AbstractEventLoop: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +if sys.version_info >= (3, 12): + _TaskCompatibleCoro: TypeAlias = Coroutine[Any, Any, _T_co] +elif sys.version_info >= (3, 9): + _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Coroutine[Any, Any, _T_co] +else: + _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co] + +# mypy and pyright complain that a subclass of an invariant class shouldn't be covariant. +# While this is true in general, here it's sort-of okay to have a covariant subclass, +# since the only reason why `asyncio.Future` is invariant is the `set_result()` method, +# and `asyncio.Task.set_result()` always raises. +class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments] + if sys.version_info >= (3, 12): + def __init__( + self, + coro: _TaskCompatibleCoro[_T_co], + *, + loop: AbstractEventLoop = ..., + name: str | None = ..., + context: Context | None = None, + eager_start: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): + def __init__( + self, + coro: _TaskCompatibleCoro[_T_co], + *, + loop: AbstractEventLoop = ..., + name: str | None = ..., + context: Context | None = None, + ) -> None: ... + else: + def __init__( + self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... + ) -> None: ... + + if sys.version_info >= (3, 12): + def get_coro(self) -> _TaskCompatibleCoro[_T_co] | None: ... + else: + def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... + + def get_name(self) -> str: ... + def set_name(self, value: object, /) -> None: ... + if sys.version_info >= (3, 12): + def get_context(self) -> Context: ... + + def get_stack(self, *, limit: int | None = None) -> list[FrameType]: ... + def print_stack(self, *, limit: int | None = None, file: TextIO | None = None) -> None: ... + if sys.version_info >= (3, 11): + def cancelling(self) -> int: ... + def uncancel(self) -> int: ... + if sys.version_info < (3, 9): + @classmethod + def current_task(cls, loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... + @classmethod + def all_tasks(cls, loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +def get_event_loop() -> AbstractEventLoop: ... +def get_running_loop() -> AbstractEventLoop: ... +def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ... +def _get_running_loop() -> AbstractEventLoop: ... +def _register_task(task: Task[Any]) -> None: ... +def _unregister_task(task: Task[Any]) -> None: ... +def _enter_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... +def _leave_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... + +if sys.version_info >= (3, 12): + def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... diff --git a/mypy/typeshed/stdlib/_blake2.pyi b/mypy/typeshed/stdlib/_blake2.pyi new file mode 100644 index 0000000000000..10d7019a222f6 --- /dev/null +++ b/mypy/typeshed/stdlib/_blake2.pyi @@ -0,0 +1,117 @@ +import sys +from _typeshed import ReadableBuffer +from typing import ClassVar, final +from typing_extensions import Self + +BLAKE2B_MAX_DIGEST_SIZE: int = 64 +BLAKE2B_MAX_KEY_SIZE: int = 64 +BLAKE2B_PERSON_SIZE: int = 16 +BLAKE2B_SALT_SIZE: int = 16 +BLAKE2S_MAX_DIGEST_SIZE: int = 32 +BLAKE2S_MAX_KEY_SIZE: int = 32 +BLAKE2S_PERSON_SIZE: int = 8 +BLAKE2S_SALT_SIZE: int = 8 + +@final +class blake2b: + MAX_DIGEST_SIZE: ClassVar[int] = 64 + MAX_KEY_SIZE: ClassVar[int] = 64 + PERSON_SIZE: ClassVar[int] = 16 + SALT_SIZE: ClassVar[int] = 16 + block_size: int + digest_size: int + name: str + if sys.version_info >= (3, 9): + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 64, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + usedforsecurity: bool = True, + ) -> None: ... + else: + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 64, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + ) -> None: ... + + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, data: ReadableBuffer, /) -> None: ... + +@final +class blake2s: + MAX_DIGEST_SIZE: ClassVar[int] = 32 + MAX_KEY_SIZE: ClassVar[int] = 32 + PERSON_SIZE: ClassVar[int] = 8 + SALT_SIZE: ClassVar[int] = 8 + block_size: int + digest_size: int + name: str + if sys.version_info >= (3, 9): + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 32, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + usedforsecurity: bool = True, + ) -> None: ... + else: + def __init__( + self, + data: ReadableBuffer = b"", + /, + *, + digest_size: int = 32, + key: ReadableBuffer = b"", + salt: ReadableBuffer = b"", + person: ReadableBuffer = b"", + fanout: int = 1, + depth: int = 1, + leaf_size: int = 0, + node_offset: int = 0, + node_depth: int = 0, + inner_size: int = 0, + last_node: bool = False, + ) -> None: ... + + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, data: ReadableBuffer, /) -> None: ... diff --git a/mypy/typeshed/stdlib/_bz2.pyi b/mypy/typeshed/stdlib/_bz2.pyi new file mode 100644 index 0000000000000..4ba26fe96be02 --- /dev/null +++ b/mypy/typeshed/stdlib/_bz2.pyi @@ -0,0 +1,18 @@ +from _typeshed import ReadableBuffer +from typing import final + +@final +class BZ2Compressor: + def __init__(self, compresslevel: int = 9) -> None: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... + def flush(self) -> bytes: ... + +@final +class BZ2Decompressor: + def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... + @property + def eof(self) -> bool: ... + @property + def needs_input(self) -> bool: ... + @property + def unused_data(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_codecs.pyi b/mypy/typeshed/stdlib/_codecs.pyi index ecf874d33ddd2..11c5d58a855ba 100644 --- a/mypy/typeshed/stdlib/_codecs.pyi +++ b/mypy/typeshed/stdlib/_codecs.pyi @@ -2,10 +2,13 @@ import codecs import sys from _typeshed import ReadableBuffer from collections.abc import Callable -from typing import Literal, overload +from typing import Literal, final, overload, type_check_only from typing_extensions import TypeAlias # This type is not exposed; it is defined in unicodeobject.c +# At runtime it calls itself builtins.EncodingMap +@final +@type_check_only class _EncodingMap: def size(self) -> int: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index e467d626e8a83..bf7f2991f9a4a 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -1,13 +1,12 @@ import sys from abc import abstractmethod from types import MappingProxyType -from typing import ( # noqa: Y022,Y038,Y057 +from typing import ( # noqa: Y022,Y038 AbstractSet as Set, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, AsyncIterator as AsyncIterator, Awaitable as Awaitable, - ByteString as ByteString, Callable as Callable, Collection as Collection, Container as Container, @@ -59,8 +58,12 @@ __all__ = [ "ValuesView", "Sequence", "MutableSequence", - "ByteString", ] +if sys.version_info < (3, 14): + from typing import ByteString as ByteString # noqa: Y057 + + __all__ += ["ByteString"] + if sys.version_info >= (3, 12): __all__ += ["Buffer"] @@ -70,12 +73,16 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. @final class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... + def __reversed__(self) -> Iterator[_KT_co]: ... + if sys.version_info >= (3, 13): + def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @final class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented + def __reversed__(self) -> Iterator[_VT_co]: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @@ -83,6 +90,9 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented @final class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented def __eq__(self, value: object, /) -> bool: ... + def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... + if sys.version_info >= (3, 13): + def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ... if sys.version_info >= (3, 10): @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... diff --git a/mypy/typeshed/stdlib/_contextvars.pyi b/mypy/typeshed/stdlib/_contextvars.pyi new file mode 100644 index 0000000000000..2e21a8c5d017d --- /dev/null +++ b/mypy/typeshed/stdlib/_contextvars.pyi @@ -0,0 +1,61 @@ +import sys +from collections.abc import Callable, Iterator, Mapping +from typing import Any, ClassVar, Generic, TypeVar, final, overload +from typing_extensions import ParamSpec + +if sys.version_info >= (3, 9): + from types import GenericAlias + +_T = TypeVar("_T") +_D = TypeVar("_D") +_P = ParamSpec("_P") + +@final +class ContextVar(Generic[_T]): + @overload + def __init__(self, name: str) -> None: ... + @overload + def __init__(self, name: str, *, default: _T) -> None: ... + def __hash__(self) -> int: ... + @property + def name(self) -> str: ... + @overload + def get(self) -> _T: ... + @overload + def get(self, default: _T, /) -> _T: ... + @overload + def get(self, default: _D, /) -> _D | _T: ... + def set(self, value: _T, /) -> Token[_T]: ... + def reset(self, token: Token[_T], /) -> None: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +@final +class Token(Generic[_T]): + @property + def var(self) -> ContextVar[_T]: ... + @property + def old_value(self) -> Any: ... # returns either _T or MISSING, but that's hard to express + MISSING: ClassVar[object] + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +def copy_context() -> Context: ... + +# It doesn't make sense to make this generic, because for most Contexts each ContextVar will have +# a different value. +@final +class Context(Mapping[ContextVar[Any], Any]): + def __init__(self) -> None: ... + @overload + def get(self, key: ContextVar[_T], default: None = None, /) -> _T | None: ... + @overload + def get(self, key: ContextVar[_T], default: _T, /) -> _T: ... + @overload + def get(self, key: ContextVar[_T], default: _D, /) -> _T | _D: ... + def run(self, callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T: ... + def copy(self) -> Context: ... + def __getitem__(self, key: ContextVar[_T], /) -> _T: ... + def __iter__(self) -> Iterator[ContextVar[Any]]: ... + def __len__(self) -> int: ... + def __eq__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index 19f2dc9664b18..afa2870be1589 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,18 +1,19 @@ +import csv import sys from _typeshed import SupportsWrite -from collections.abc import Iterable, Iterator -from typing import Any, Final, Literal -from typing_extensions import TypeAlias +from collections.abc import Iterable +from typing import Any, Final, type_check_only +from typing_extensions import Self, TypeAlias __version__: Final[str] -QUOTE_ALL: Literal[1] -QUOTE_MINIMAL: Literal[0] -QUOTE_NONE: Literal[3] -QUOTE_NONNUMERIC: Literal[2] +QUOTE_ALL: Final = 1 +QUOTE_MINIMAL: Final = 0 +QUOTE_NONE: Final = 3 +QUOTE_NONNUMERIC: Final = 2 if sys.version_info >= (3, 12): - QUOTE_STRINGS: Literal[4] - QUOTE_NOTNULL: Literal[5] + QUOTE_STRINGS: Final = 4 + QUOTE_NOTNULL: Final = 5 # Ideally this would be `QUOTE_ALL | QUOTE_MINIMAL | QUOTE_NONE | QUOTE_NONNUMERIC` # However, using literals in situations like these can cause false-positives (see #7258) @@ -20,6 +21,8 @@ _QuotingType: TypeAlias = int class Error(Exception): ... +_DialectLike: TypeAlias = str | Dialect | csv.Dialect | type[Dialect | csv.Dialect] + class Dialect: delimiter: str quotechar: str | None @@ -29,21 +32,60 @@ class Dialect: lineterminator: str quoting: _QuotingType strict: bool - def __init__(self) -> None: ... + def __init__( + self, + dialect: _DialectLike | None = ..., + delimiter: str = ",", + doublequote: bool = True, + escapechar: str | None = None, + lineterminator: str = "\r\n", + quotechar: str | None = '"', + quoting: _QuotingType = 0, + skipinitialspace: bool = False, + strict: bool = False, + ) -> None: ... + +if sys.version_info >= (3, 10): + # This class calls itself _csv.reader. + class Reader: + @property + def dialect(self) -> Dialect: ... + line_num: int + def __iter__(self) -> Self: ... + def __next__(self) -> list[str]: ... -_DialectLike: TypeAlias = str | Dialect | type[Dialect] + # This class calls itself _csv.writer. + class Writer: + @property + def dialect(self) -> Dialect: ... + if sys.version_info >= (3, 13): + def writerow(self, row: Iterable[Any], /) -> Any: ... + def writerows(self, rows: Iterable[Iterable[Any]], /) -> None: ... + else: + def writerow(self, row: Iterable[Any]) -> Any: ... + def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ... -class _reader(Iterator[list[str]]): - @property - def dialect(self) -> Dialect: ... - line_num: int - def __next__(self) -> list[str]: ... + # For the return types below. + # These aliases can be removed when typeshed drops support for 3.9. + _reader = Reader + _writer = Writer +else: + # This class is not exposed. It calls itself _csv.reader. + @type_check_only + class _reader: + @property + def dialect(self) -> Dialect: ... + line_num: int + def __iter__(self) -> Self: ... + def __next__(self) -> list[str]: ... -class _writer: - @property - def dialect(self) -> Dialect: ... - def writerow(self, row: Iterable[Any]) -> Any: ... - def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ... + # This class is not exposed. It calls itself _csv.writer. + @type_check_only + class _writer: + @property + def dialect(self) -> Dialect: ... + def writerow(self, row: Iterable[Any]) -> Any: ... + def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ... def writer( csvfile: SupportsWrite[str], diff --git a/mypy/typeshed/stdlib/_ctypes.pyi b/mypy/typeshed/stdlib/_ctypes.pyi index a5f20dfd30e7f..ecb07a29bb752 100644 --- a/mypy/typeshed/stdlib/_ctypes.pyi +++ b/mypy/typeshed/stdlib/_ctypes.pyi @@ -1,9 +1,10 @@ +import _typeshed import sys -from _typeshed import ReadableBuffer, WriteableBuffer +from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from abc import abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from ctypes import CDLL, ArgumentError as ArgumentError -from typing import Any, ClassVar, Generic, TypeVar, overload +from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p +from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -47,47 +48,79 @@ if sys.platform == "win32": def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ... def FreeLibrary(handle: int, /) -> None: ... -class _CDataMeta(type): - # By default mypy complains about the following two methods, because strictly speaking cls - # might not be a Type[_CT]. However this can never actually happen, because the only class that - # uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here. - def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] - def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] +else: + def dlclose(handle: int, /) -> None: ... + # The default for flag is RTLD_GLOBAL|RTLD_LOCAL, which is platform dependent. + def dlopen(name: StrOrBytesPath, flag: int = ..., /) -> int: ... + def dlsym(handle: int, name: str, /) -> int: ... -class _CData(metaclass=_CDataMeta): +if sys.version_info >= (3, 13): + # This class is not exposed. It calls itself _ctypes.CType_Type. + @type_check_only + class _CType_Type(type): + # By default mypy complains about the following two methods, because strictly speaking cls + # might not be a Type[_CT]. However this doesn't happen because this is only a + # metaclass for subclasses of _CData. + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + + _CTypeBaseType = _CType_Type + +else: + _CTypeBaseType = type + +# This class is not exposed. +@type_check_only +class _CData: _b_base_: int _b_needsfree_: bool _objects: Mapping[Any, int] | None - # At runtime the following classmethods are available only on classes, not - # on instances. This can't be reflected properly in the type system: - # - # Structure.from_buffer(...) # valid at runtime - # Structure(...).from_buffer(...) # invalid at runtime - # - - @classmethod - def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ... - @classmethod - def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ... - @classmethod - def from_address(cls, address: int) -> Self: ... - @classmethod - def from_param(cls, obj: Any) -> Self | _CArgObject: ... - @classmethod - def in_dll(cls, library: CDLL, name: str) -> Self: ... def __buffer__(self, flags: int, /) -> memoryview: ... - def __release_buffer__(self, buffer: memoryview, /) -> None: ... + def __ctypes_from_outparam__(self, /) -> Self: ... -class _SimpleCData(_CData, Generic[_T]): +# this is a union of all the subclasses of _CData, which is useful because of +# the methods that are present on each of those subclasses which are not present +# on _CData itself. +_CDataType: TypeAlias = _SimpleCData[Any] | _Pointer[Any] | CFuncPtr | Union | Structure | Array[Any] + +# This class is not exposed. It calls itself _ctypes.PyCSimpleType. +@type_check_only +class _PyCSimpleType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class _SimpleCData(_CData, Generic[_T], metaclass=_PyCSimpleType): value: _T # The TypeVar can be unsolved here, # but we can't use overloads without creating many, many mypy false-positive errors def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] + def __ctypes_from_outparam__(self, /) -> _T: ... # type: ignore[override] class _CanCastTo(_CData): ... class _PointerLike(_CanCastTo): ... -class _Pointer(_PointerLike, _CData, Generic[_CT]): +# This type is not exposed. It calls itself _ctypes.PyCPointerType. +@type_check_only +class _PyCPointerType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + def set_type(self, type: Any, /) -> None: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class _Pointer(_PointerLike, _CData, Generic[_CT], metaclass=_PyCPointerType): _type_: type[_CT] contents: _CT @overload @@ -100,19 +133,38 @@ class _Pointer(_PointerLike, _CData, Generic[_CT]): def __getitem__(self, key: slice, /) -> list[Any]: ... def __setitem__(self, key: int, value: Any, /) -> None: ... -def POINTER(type: type[_CT]) -> type[_Pointer[_CT]]: ... -def pointer(arg: _CT, /) -> _Pointer[_CT]: ... +@overload +def POINTER(type: None, /) -> type[c_void_p]: ... +@overload +def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ... +def pointer(obj: _CT, /) -> _Pointer[_CT]: ... +# This class is not exposed. It calls itself _ctypes.CArgObject. +@final +@type_check_only class _CArgObject: ... -def byref(obj: _CData, offset: int = ...) -> _CArgObject: ... +def byref(obj: _CData | _CDataType, offset: int = ...) -> _CArgObject: ... -_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData] +_ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType] _PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any] -class CFuncPtr(_PointerLike, _CData): - restype: type[_CData] | Callable[[int], Any] | None - argtypes: Sequence[type[_CData]] +# This class is not exposed. It calls itself _ctypes.PyCFuncPtrType. +@type_check_only +class _PyCFuncPtrType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType): + restype: type[_CDataType] | Callable[[int], Any] | None + argtypes: Sequence[type[_CDataType]] errcheck: _ECT # Abstract attribute that must be defined on subclasses _flags_: ClassVar[int] @@ -127,7 +179,7 @@ class CFuncPtr(_PointerLike, _CData): if sys.platform == "win32": @overload def __init__( - self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., / + self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., / ) -> None: ... def __call__(self, *args: Any, **kwargs: Any) -> Any: ... @@ -135,30 +187,95 @@ class CFuncPtr(_PointerLike, _CData): _GetT = TypeVar("_GetT") _SetT = TypeVar("_SetT") +# This class is not exposed. It calls itself _ctypes.CField. +@final +@type_check_only class _CField(Generic[_CT, _GetT, _SetT]): offset: int size: int - @overload - def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ... - @overload - def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ... + if sys.version_info >= (3, 10): + @overload + def __get__(self, instance: None, owner: type[Any] | None = None, /) -> Self: ... + @overload + def __get__(self, instance: Any, owner: type[Any] | None = None, /) -> _GetT: ... + else: + @overload + def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ... + @overload + def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ... + def __set__(self, instance: Any, value: _SetT, /) -> None: ... -class _StructUnionMeta(_CDataMeta): - _fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]] - _pack_: int - _anonymous_: Sequence[str] +# This class is not exposed. It calls itself _ctypes.UnionType. +@type_check_only +class _UnionType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + # At runtime, various attributes are created on a Union subclass based + # on its _fields_. This method doesn't exist, but represents those + # dynamically created attributes. def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class Union(_CData, metaclass=_UnionType): + _fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]] + _pack_: ClassVar[int] + _anonymous_: ClassVar[Sequence[str]] + if sys.version_info >= (3, 13): + _align_: ClassVar[int] + + def __init__(self, *args: Any, **kw: Any) -> None: ... + def __getattr__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + +# This class is not exposed. It calls itself _ctypes.PyCStructType. +@type_check_only +class _PyCStructType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + # At runtime, various attributes are created on a Structure subclass based + # on its _fields_. This method doesn't exist, but represents those + # dynamically created attributes. + def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class Structure(_CData, metaclass=_PyCStructType): + _fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]] + _pack_: ClassVar[int] + _anonymous_: ClassVar[Sequence[str]] + if sys.version_info >= (3, 13): + _align_: ClassVar[int] -class _StructUnionBase(_CData, metaclass=_StructUnionMeta): def __init__(self, *args: Any, **kw: Any) -> None: ... def __getattr__(self, name: str) -> Any: ... def __setattr__(self, name: str, value: Any) -> None: ... -class Union(_StructUnionBase): ... -class Structure(_StructUnionBase): ... +# This class is not exposed. It calls itself _ctypes.PyCArrayType. +@type_check_only +class _PyCArrayType(_CTypeBaseType): + def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ... + def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ... + def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ... + def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ... + if sys.version_info < (3, 13): + # Inherited from CType_Type starting on 3.13 + def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] -class Array(_CData, Generic[_CT]): +class Array(_CData, Generic[_CT], metaclass=_PyCArrayType): @property @abstractmethod def _length_(self) -> int: ... @@ -199,9 +316,15 @@ class Array(_CData, Generic[_CT]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -def addressof(obj: _CData) -> int: ... -def alignment(obj_or_type: _CData | type[_CData]) -> int: ... +def addressof(obj: _CData | _CDataType, /) -> int: ... +def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ... def get_errno() -> int: ... -def resize(obj: _CData, size: int) -> None: ... -def set_errno(value: int) -> int: ... -def sizeof(obj_or_type: _CData | type[_CData]) -> int: ... +def resize(obj: _CData | _CDataType, size: int, /) -> None: ... +def set_errno(value: int, /) -> int: ... +def sizeof(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ... +def PyObj_FromPtr(address: int, /) -> Any: ... +def Py_DECREF(o: _T, /) -> _T: ... +def Py_INCREF(o: _T, /) -> _T: ... +def buffer_info(o: _CData | _CDataType | type[_CData | _CDataType], /) -> tuple[str, int, tuple[int, ...]]: ... +def call_cdeclfunction(address: int, arguments: tuple[Any, ...], /) -> Any: ... +def call_function(address: int, arguments: tuple[Any, ...], /) -> Any: ... diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index eb1d7b9bde9f3..9e06a1414da51 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import ReadOnlyBuffer, SupportsRead -from typing import IO, Any, NamedTuple, final, overload +from curses import _ncurses_version +from typing import IO, Any, final, overload from typing_extensions import TypeAlias # NOTE: This module is ordinarily only available on Unix, but the windows-curses @@ -298,7 +299,7 @@ if sys.version_info >= (3, 9): def getmouse() -> tuple[int, int, int, int, int]: ... def getsyx() -> tuple[int, int]: ... -def getwin(file: SupportsRead[bytes], /) -> _CursesWindow: ... +def getwin(file: SupportsRead[bytes], /) -> window: ... def halfdelay(tenths: int, /) -> None: ... def has_colors() -> bool: ... @@ -310,7 +311,7 @@ def has_il() -> bool: ... def has_key(key: int, /) -> bool: ... def init_color(color_number: int, r: int, g: int, b: int, /) -> None: ... def init_pair(pair_number: int, fg: int, bg: int, /) -> None: ... -def initscr() -> _CursesWindow: ... +def initscr() -> window: ... def intrflush(flag: bool, /) -> None: ... def is_term_resized(nlines: int, ncols: int, /) -> bool: ... def isendwin() -> bool: ... @@ -321,8 +322,8 @@ def meta(yes: bool, /) -> None: ... def mouseinterval(interval: int, /) -> None: ... def mousemask(newmask: int, /) -> tuple[int, int]: ... def napms(ms: int, /) -> int: ... -def newpad(nlines: int, ncols: int, /) -> _CursesWindow: ... -def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> _CursesWindow: ... +def newpad(nlines: int, ncols: int, /) -> window: ... +def newwin(nlines: int, ncols: int, begin_y: int = ..., begin_x: int = ..., /) -> window: ... def nl(flag: bool = True, /) -> None: ... def nocbreak() -> None: ... def noecho() -> None: ... @@ -368,11 +369,7 @@ def tparm( ) -> bytes: ... def typeahead(fd: int, /) -> None: ... def unctrl(ch: _ChType, /) -> bytes: ... - -if sys.version_info < (3, 12) or sys.platform != "darwin": - # The support for macos was dropped in 3.12 - def unget_wch(ch: int | str, /) -> None: ... - +def unget_wch(ch: int | str, /) -> None: ... def ungetch(ch: _ChType, /) -> None: ... def ungetmouse(id: int, x: int, y: int, z: int, bstate: int, /) -> None: ... def update_lines_cols() -> None: ... @@ -382,7 +379,7 @@ def use_env(flag: bool, /) -> None: ... class error(Exception): ... @final -class _CursesWindow: +class window: # undocumented encoding: str @overload def addch(self, ch: _ChType, attr: int = ...) -> None: ... @@ -435,9 +432,9 @@ class _CursesWindow: def delch(self, y: int, x: int) -> None: ... def deleteln(self) -> None: ... @overload - def derwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + def derwin(self, begin_y: int, begin_x: int) -> window: ... @overload - def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def derwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ... def echochar(self, ch: _ChType, attr: int = ..., /) -> None: ... def enclose(self, y: int, x: int, /) -> bool: ... def erase(self) -> None: ... @@ -447,13 +444,10 @@ class _CursesWindow: def getch(self) -> int: ... @overload def getch(self, y: int, x: int) -> int: ... - if sys.version_info < (3, 12) or sys.platform != "darwin": - # The support for macos was dropped in 3.12 - @overload - def get_wch(self) -> int | str: ... - @overload - def get_wch(self, y: int, x: int) -> int | str: ... - + @overload + def get_wch(self) -> int | str: ... + @overload + def get_wch(self, y: int, x: int) -> int | str: ... @overload def getkey(self) -> str: ... @overload @@ -500,7 +494,7 @@ class _CursesWindow: def instr(self, y: int, x: int, n: int = ...) -> bytes: ... def is_linetouched(self, line: int, /) -> bool: ... def is_wintouched(self) -> bool: ... - def keypad(self, yes: bool) -> None: ... + def keypad(self, yes: bool, /) -> None: ... def leaveok(self, yes: bool) -> None: ... def move(self, new_y: int, new_x: int) -> None: ... def mvderwin(self, y: int, x: int) -> None: ... @@ -512,16 +506,16 @@ class _CursesWindow: @overload def noutrefresh(self, pminrow: int, pmincol: int, sminrow: int, smincol: int, smaxrow: int, smaxcol: int) -> None: ... @overload - def overlay(self, destwin: _CursesWindow) -> None: ... + def overlay(self, destwin: window) -> None: ... @overload def overlay( - self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int + self, destwin: window, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int ) -> None: ... @overload - def overwrite(self, destwin: _CursesWindow) -> None: ... + def overwrite(self, destwin: window) -> None: ... @overload def overwrite( - self, destwin: _CursesWindow, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int + self, destwin: window, sminrow: int, smincol: int, dminrow: int, dmincol: int, dmaxrow: int, dmaxcol: int ) -> None: ... def putwin(self, file: IO[Any], /) -> None: ... def redrawln(self, beg: int, num: int, /) -> None: ... @@ -537,13 +531,13 @@ class _CursesWindow: def standend(self) -> None: ... def standout(self) -> None: ... @overload - def subpad(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subpad(self, begin_y: int, begin_x: int) -> window: ... @overload - def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subpad(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ... @overload - def subwin(self, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subwin(self, begin_y: int, begin_x: int) -> window: ... @overload - def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> _CursesWindow: ... + def subwin(self, nlines: int, ncols: int, begin_y: int, begin_x: int) -> window: ... def syncdown(self) -> None: ... def syncok(self, flag: bool) -> None: ... def syncup(self) -> None: ... @@ -556,10 +550,4 @@ class _CursesWindow: @overload def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ... -class _ncurses_version(NamedTuple): - major: int - minor: int - patch: int - ncurses_version: _ncurses_version -window = _CursesWindow # undocumented diff --git a/mypy/typeshed/stdlib/_curses_panel.pyi b/mypy/typeshed/stdlib/_curses_panel.pyi new file mode 100644 index 0000000000000..ddec22236b963 --- /dev/null +++ b/mypy/typeshed/stdlib/_curses_panel.pyi @@ -0,0 +1,27 @@ +from _curses import window +from typing import final + +__version__: str +version: str + +class error(Exception): ... + +@final +class panel: + def above(self) -> panel: ... + def below(self) -> panel: ... + def bottom(self) -> None: ... + def hidden(self) -> bool: ... + def hide(self) -> None: ... + def move(self, y: int, x: int, /) -> None: ... + def replace(self, win: window, /) -> None: ... + def set_userptr(self, obj: object, /) -> None: ... + def show(self) -> None: ... + def top(self) -> None: ... + def userptr(self) -> object: ... + def window(self) -> window: ... + +def bottom_panel() -> panel: ... +def new_panel(win: window, /) -> panel: ... +def top_panel() -> panel: ... +def update_panels() -> panel: ... diff --git a/mypy/typeshed/stdlib/_dbm.pyi b/mypy/typeshed/stdlib/_dbm.pyi new file mode 100644 index 0000000000000..7e53cca3c704f --- /dev/null +++ b/mypy/typeshed/stdlib/_dbm.pyi @@ -0,0 +1,44 @@ +import sys +from _typeshed import ReadOnlyBuffer, StrOrBytesPath +from types import TracebackType +from typing import TypeVar, final, overload, type_check_only +from typing_extensions import Self, TypeAlias + +if sys.platform != "win32": + _T = TypeVar("_T") + _KeyType: TypeAlias = str | ReadOnlyBuffer + _ValueType: TypeAlias = str | ReadOnlyBuffer + + class error(OSError): ... + library: str + + # Actual typename dbm, not exposed by the implementation + @final + @type_check_only + class _dbm: + def close(self) -> None: ... + if sys.version_info >= (3, 13): + def clear(self) -> None: ... + + def __getitem__(self, item: _KeyType) -> bytes: ... + def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... + def __delitem__(self, key: _KeyType) -> None: ... + def __len__(self) -> int: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + @overload + def get(self, k: _KeyType, /) -> bytes | None: ... + @overload + def get(self, k: _KeyType, default: _T, /) -> bytes | _T: ... + def keys(self) -> list[bytes]: ... + def setdefault(self, k: _KeyType, default: _ValueType = ..., /) -> bytes: ... + # This isn't true, but the class can't be instantiated. See #13024 + __new__: None # type: ignore[assignment] + __init__: None # type: ignore[assignment] + + if sys.version_info >= (3, 11): + def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... + else: + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index 90d16215c280d..cdd0268a1bdff 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -1,51 +1,46 @@ -import numbers import sys -from collections.abc import Container, Sequence -from types import TracebackType -from typing import Any, ClassVar, Final, Literal, NamedTuple, overload -from typing_extensions import Self, TypeAlias +from decimal import ( + Clamped as Clamped, + Context as Context, + ConversionSyntax as ConversionSyntax, + Decimal as Decimal, + DecimalException as DecimalException, + DecimalTuple as DecimalTuple, + DivisionByZero as DivisionByZero, + DivisionImpossible as DivisionImpossible, + DivisionUndefined as DivisionUndefined, + FloatOperation as FloatOperation, + Inexact as Inexact, + InvalidContext as InvalidContext, + InvalidOperation as InvalidOperation, + Overflow as Overflow, + Rounded as Rounded, + Subnormal as Subnormal, + Underflow as Underflow, + _ContextManager, +) +from typing import Final +from typing_extensions import TypeAlias -_Decimal: TypeAlias = Decimal | int -_DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int] -_ComparableNum: TypeAlias = Decimal | float | numbers.Rational +_TrapType: TypeAlias = type[DecimalException] __version__: Final[str] __libmpdec_version__: Final[str] -class DecimalTuple(NamedTuple): - sign: int - digits: tuple[int, ...] - exponent: int | Literal["n", "N", "F"] - -ROUND_DOWN: str -ROUND_HALF_UP: str -ROUND_HALF_EVEN: str -ROUND_CEILING: str -ROUND_FLOOR: str -ROUND_UP: str -ROUND_HALF_DOWN: str -ROUND_05UP: str -HAVE_CONTEXTVAR: bool -HAVE_THREADS: bool -MAX_EMAX: int -MAX_PREC: int -MIN_EMIN: int -MIN_ETINY: int - -class DecimalException(ArithmeticError): ... -class Clamped(DecimalException): ... -class InvalidOperation(DecimalException): ... -class ConversionSyntax(InvalidOperation): ... -class DivisionByZero(DecimalException, ZeroDivisionError): ... -class DivisionImpossible(InvalidOperation): ... -class DivisionUndefined(InvalidOperation, ZeroDivisionError): ... -class Inexact(DecimalException): ... -class InvalidContext(InvalidOperation): ... -class Rounded(DecimalException): ... -class Subnormal(DecimalException): ... -class Overflow(Inexact, Rounded): ... -class Underflow(Inexact, Rounded, Subnormal): ... -class FloatOperation(DecimalException, TypeError): ... +ROUND_DOWN: Final[str] +ROUND_HALF_UP: Final[str] +ROUND_HALF_EVEN: Final[str] +ROUND_CEILING: Final[str] +ROUND_FLOOR: Final[str] +ROUND_UP: Final[str] +ROUND_HALF_DOWN: Final[str] +ROUND_05UP: Final[str] +HAVE_CONTEXTVAR: Final[bool] +HAVE_THREADS: Final[bool] +MAX_EMAX: Final[int] +MAX_PREC: Final[int] +MIN_EMIN: Final[int] +MIN_ETINY: Final[int] def setcontext(context: Context, /) -> None: ... def getcontext() -> Context: ... @@ -67,215 +62,6 @@ if sys.version_info >= (3, 11): else: def localcontext(ctx: Context | None = None) -> _ContextManager: ... -class Decimal: - def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... - @classmethod - def from_float(cls, f: float, /) -> Self: ... - def __bool__(self) -> bool: ... - def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __hash__(self) -> int: ... - def as_tuple(self) -> DecimalTuple: ... - def as_integer_ratio(self) -> tuple[int, int]: ... - def to_eng_string(self, context: Context | None = None) -> str: ... - def __abs__(self) -> Decimal: ... - def __add__(self, value: _Decimal, /) -> Decimal: ... - def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def __eq__(self, value: object, /) -> bool: ... - def __floordiv__(self, value: _Decimal, /) -> Decimal: ... - def __ge__(self, value: _ComparableNum, /) -> bool: ... - def __gt__(self, value: _ComparableNum, /) -> bool: ... - def __le__(self, value: _ComparableNum, /) -> bool: ... - def __lt__(self, value: _ComparableNum, /) -> bool: ... - def __mod__(self, value: _Decimal, /) -> Decimal: ... - def __mul__(self, value: _Decimal, /) -> Decimal: ... - def __neg__(self) -> Decimal: ... - def __pos__(self) -> Decimal: ... - def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ... - def __radd__(self, value: _Decimal, /) -> Decimal: ... - def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ... - def __rmod__(self, value: _Decimal, /) -> Decimal: ... - def __rmul__(self, value: _Decimal, /) -> Decimal: ... - def __rsub__(self, value: _Decimal, /) -> Decimal: ... - def __rtruediv__(self, value: _Decimal, /) -> Decimal: ... - def __sub__(self, value: _Decimal, /) -> Decimal: ... - def __truediv__(self, value: _Decimal, /) -> Decimal: ... - def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __float__(self) -> float: ... - def __int__(self) -> int: ... - def __trunc__(self) -> int: ... - @property - def real(self) -> Decimal: ... - @property - def imag(self) -> Decimal: ... - def conjugate(self) -> Decimal: ... - def __complex__(self) -> complex: ... - @overload - def __round__(self) -> int: ... - @overload - def __round__(self, ndigits: int, /) -> Decimal: ... - def __floor__(self) -> int: ... - def __ceil__(self) -> int: ... - def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ... - def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ... - def normalize(self, context: Context | None = None) -> Decimal: ... - def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ... - def to_integral_exact(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def to_integral_value(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def to_integral(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... - def sqrt(self, context: Context | None = None) -> Decimal: ... - def max(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def min(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def adjusted(self) -> int: ... - def canonical(self) -> Decimal: ... - def compare_signal(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def compare_total(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def compare_total_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def copy_abs(self) -> Decimal: ... - def copy_negate(self) -> Decimal: ... - def copy_sign(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def exp(self, context: Context | None = None) -> Decimal: ... - def is_canonical(self) -> bool: ... - def is_finite(self) -> bool: ... - def is_infinite(self) -> bool: ... - def is_nan(self) -> bool: ... - def is_normal(self, context: Context | None = None) -> bool: ... - def is_qnan(self) -> bool: ... - def is_signed(self) -> bool: ... - def is_snan(self) -> bool: ... - def is_subnormal(self, context: Context | None = None) -> bool: ... - def is_zero(self) -> bool: ... - def ln(self, context: Context | None = None) -> Decimal: ... - def log10(self, context: Context | None = None) -> Decimal: ... - def logb(self, context: Context | None = None) -> Decimal: ... - def logical_and(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def logical_invert(self, context: Context | None = None) -> Decimal: ... - def logical_or(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def logical_xor(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def max_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def min_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def next_minus(self, context: Context | None = None) -> Decimal: ... - def next_plus(self, context: Context | None = None) -> Decimal: ... - def next_toward(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def number_class(self, context: Context | None = None) -> str: ... - def radix(self) -> Decimal: ... - def rotate(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def scaleb(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ... - def __reduce__(self) -> tuple[type[Self], tuple[str]]: ... - def __copy__(self) -> Self: ... - def __deepcopy__(self, memo: Any, /) -> Self: ... - def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ... - -class _ContextManager: - new_context: Context - saved_context: Context - def __init__(self, new_context: Context) -> None: ... - def __enter__(self) -> Context: ... - def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... - -_TrapType: TypeAlias = type[DecimalException] - -class Context: - # TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime, - # even settable attributes like `prec` and `rounding`, - # but that's inexpressable in the stub. - # Type checkers either ignore it or misinterpret it - # if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub - prec: int - rounding: str - Emin: int - Emax: int - capitals: int - clamp: int - traps: dict[_TrapType, bool] - flags: dict[_TrapType, bool] - def __init__( - self, - prec: int | None = ..., - rounding: str | None = ..., - Emin: int | None = ..., - Emax: int | None = ..., - capitals: int | None = ..., - clamp: int | None = ..., - flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - _ignored_flags: list[_TrapType] | None = ..., - ) -> None: ... - def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... - def clear_flags(self) -> None: ... - def clear_traps(self) -> None: ... - def copy(self) -> Context: ... - def __copy__(self) -> Context: ... - # see https://github.com/python/cpython/issues/94107 - __hash__: ClassVar[None] # type: ignore[assignment] - def Etiny(self) -> int: ... - def Etop(self) -> int: ... - def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ... - def create_decimal_from_float(self, f: float, /) -> Decimal: ... - def abs(self, x: _Decimal, /) -> Decimal: ... - def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def canonical(self, x: Decimal, /) -> Decimal: ... - def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def copy_abs(self, x: _Decimal, /) -> Decimal: ... - def copy_decimal(self, x: _Decimal, /) -> Decimal: ... - def copy_negate(self, x: _Decimal, /) -> Decimal: ... - def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ... - def exp(self, x: _Decimal, /) -> Decimal: ... - def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ... - def is_canonical(self, x: _Decimal, /) -> bool: ... - def is_finite(self, x: _Decimal, /) -> bool: ... - def is_infinite(self, x: _Decimal, /) -> bool: ... - def is_nan(self, x: _Decimal, /) -> bool: ... - def is_normal(self, x: _Decimal, /) -> bool: ... - def is_qnan(self, x: _Decimal, /) -> bool: ... - def is_signed(self, x: _Decimal, /) -> bool: ... - def is_snan(self, x: _Decimal, /) -> bool: ... - def is_subnormal(self, x: _Decimal, /) -> bool: ... - def is_zero(self, x: _Decimal, /) -> bool: ... - def ln(self, x: _Decimal, /) -> Decimal: ... - def log10(self, x: _Decimal, /) -> Decimal: ... - def logb(self, x: _Decimal, /) -> Decimal: ... - def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def logical_invert(self, x: _Decimal, /) -> Decimal: ... - def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def minus(self, x: _Decimal, /) -> Decimal: ... - def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def next_minus(self, x: _Decimal, /) -> Decimal: ... - def next_plus(self, x: _Decimal, /) -> Decimal: ... - def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def normalize(self, x: _Decimal, /) -> Decimal: ... - def number_class(self, x: _Decimal, /) -> str: ... - def plus(self, x: _Decimal, /) -> Decimal: ... - def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ... - def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def radix(self) -> Decimal: ... - def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ... - def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def sqrt(self, x: _Decimal, /) -> Decimal: ... - def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... - def to_eng_string(self, x: _Decimal, /) -> str: ... - def to_sci_string(self, x: _Decimal, /) -> str: ... - def to_integral_exact(self, x: _Decimal, /) -> Decimal: ... - def to_integral_value(self, x: _Decimal, /) -> Decimal: ... - def to_integral(self, x: _Decimal, /) -> Decimal: ... - DefaultContext: Context BasicContext: Context ExtendedContext: Context diff --git a/mypy/typeshed/stdlib/_frozen_importlib.pyi b/mypy/typeshed/stdlib/_frozen_importlib.pyi new file mode 100644 index 0000000000000..b6d7a18420482 --- /dev/null +++ b/mypy/typeshed/stdlib/_frozen_importlib.pyi @@ -0,0 +1,112 @@ +import importlib.abc +import importlib.machinery +import sys +import types +from _typeshed.importlib import LoaderProtocol +from collections.abc import Mapping, Sequence +from types import ModuleType +from typing import Any + +# Signature of `builtins.__import__` should be kept identical to `importlib.__import__` +def __import__( + name: str, + globals: Mapping[str, object] | None = None, + locals: Mapping[str, object] | None = None, + fromlist: Sequence[str] = (), + level: int = 0, +) -> ModuleType: ... +def spec_from_loader( + name: str, loader: LoaderProtocol | None, *, origin: str | None = None, is_package: bool | None = None +) -> importlib.machinery.ModuleSpec | None: ... +def module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType: ... +def _init_module_attrs( + spec: importlib.machinery.ModuleSpec, module: types.ModuleType, *, override: bool = False +) -> types.ModuleType: ... + +class ModuleSpec: + def __init__( + self, + name: str, + loader: importlib.abc.Loader | None, + *, + origin: str | None = None, + loader_state: Any = None, + is_package: bool | None = None, + ) -> None: ... + name: str + loader: importlib.abc.Loader | None + origin: str | None + submodule_search_locations: list[str] | None + loader_state: Any + cached: str | None + @property + def parent(self) -> str | None: ... + has_location: bool + def __eq__(self, other: object) -> bool: ... + +class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): + # MetaPathFinder + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + # InspectLoader + @classmethod + def is_package(cls, fullname: str) -> bool: ... + @classmethod + def load_module(cls, fullname: str) -> types.ModuleType: ... + @classmethod + def get_code(cls, fullname: str) -> None: ... + @classmethod + def get_source(cls, fullname: str) -> None: ... + # Loader + if sys.version_info < (3, 12): + @staticmethod + def module_repr(module: types.ModuleType) -> str: ... + if sys.version_info >= (3, 10): + @staticmethod + def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... + @staticmethod + def exec_module(module: types.ModuleType) -> None: ... + else: + @classmethod + def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... + @classmethod + def exec_module(cls, module: types.ModuleType) -> None: ... + +class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): + # MetaPathFinder + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + # InspectLoader + @classmethod + def is_package(cls, fullname: str) -> bool: ... + @classmethod + def load_module(cls, fullname: str) -> types.ModuleType: ... + @classmethod + def get_code(cls, fullname: str) -> None: ... + @classmethod + def get_source(cls, fullname: str) -> None: ... + # Loader + if sys.version_info < (3, 12): + @staticmethod + def module_repr(m: types.ModuleType) -> str: ... + if sys.version_info >= (3, 10): + @staticmethod + def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... + else: + @classmethod + def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... + + @staticmethod + def exec_module(module: types.ModuleType) -> None: ... diff --git a/mypy/typeshed/stdlib/_frozen_importlib_external.pyi b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi new file mode 100644 index 0000000000000..d3127666da301 --- /dev/null +++ b/mypy/typeshed/stdlib/_frozen_importlib_external.pyi @@ -0,0 +1,178 @@ +import _ast +import _io +import importlib.abc +import importlib.machinery +import sys +import types +from _typeshed import ReadableBuffer, StrOrBytesPath, StrPath +from _typeshed.importlib import LoaderProtocol +from collections.abc import Callable, Iterable, Iterator, Mapping, MutableSequence, Sequence +from importlib.machinery import ModuleSpec +from importlib.metadata import DistributionFinder, PathDistribution +from typing import Any, Literal +from typing_extensions import Self, deprecated + +if sys.version_info >= (3, 10): + import importlib.readers + +if sys.platform == "win32": + path_separators: Literal["\\/"] + path_sep: Literal["\\"] + path_sep_tuple: tuple[Literal["\\"], Literal["/"]] +else: + path_separators: Literal["/"] + path_sep: Literal["/"] + path_sep_tuple: tuple[Literal["/"]] + +MAGIC_NUMBER: bytes + +def cache_from_source(path: str, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ... +def source_from_cache(path: str) -> str: ... +def decode_source(source_bytes: ReadableBuffer) -> str: ... +def spec_from_file_location( + name: str, + location: StrOrBytesPath | None = None, + *, + loader: LoaderProtocol | None = None, + submodule_search_locations: list[str] | None = ..., +) -> importlib.machinery.ModuleSpec | None: ... + +class WindowsRegistryFinder(importlib.abc.MetaPathFinder): + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + +class PathFinder(importlib.abc.MetaPathFinder): + if sys.version_info >= (3, 10): + @staticmethod + def invalidate_caches() -> None: ... + else: + @classmethod + def invalidate_caches(cls) -> None: ... + if sys.version_info >= (3, 10): + @staticmethod + def find_distributions(context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... + else: + @classmethod + def find_distributions(cls, context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... + + @classmethod + def find_spec( + cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None + ) -> ModuleSpec | None: ... + if sys.version_info < (3, 12): + @classmethod + def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... + +SOURCE_SUFFIXES: list[str] +DEBUG_BYTECODE_SUFFIXES: list[str] +OPTIMIZED_BYTECODE_SUFFIXES: list[str] +BYTECODE_SUFFIXES: list[str] +EXTENSION_SUFFIXES: list[str] + +class FileFinder(importlib.abc.PathEntryFinder): + path: str + def __init__(self, path: str, *loader_details: tuple[type[importlib.abc.Loader], list[str]]) -> None: ... + @classmethod + def path_hook( + cls, *loader_details: tuple[type[importlib.abc.Loader], list[str]] + ) -> Callable[[str], importlib.abc.PathEntryFinder]: ... + +class _LoaderBasics: + def is_package(self, fullname: str) -> bool: ... + def create_module(self, spec: ModuleSpec) -> types.ModuleType | None: ... + def exec_module(self, module: types.ModuleType) -> None: ... + def load_module(self, fullname: str) -> types.ModuleType: ... + +class SourceLoader(_LoaderBasics): + def path_mtime(self, path: str) -> float: ... + def set_data(self, path: str, data: bytes) -> None: ... + def get_source(self, fullname: str) -> str | None: ... + def path_stats(self, path: str) -> Mapping[str, Any]: ... + def source_to_code( + self, data: ReadableBuffer | str | _ast.Module | _ast.Expression | _ast.Interactive, path: ReadableBuffer | StrPath + ) -> types.CodeType: ... + def get_code(self, fullname: str) -> types.CodeType | None: ... + +class FileLoader: + name: str + path: str + def __init__(self, fullname: str, path: str) -> None: ... + def get_data(self, path: str) -> bytes: ... + def get_filename(self, name: str | None = None) -> str: ... + def load_module(self, name: str | None = None) -> types.ModuleType: ... + if sys.version_info >= (3, 10): + def get_resource_reader(self, name: str | None = None) -> importlib.readers.FileReader: ... + else: + def get_resource_reader(self, name: str | None = None) -> Self | None: ... + def open_resource(self, resource: str) -> _io.FileIO: ... + def resource_path(self, resource: str) -> str: ... + def is_resource(self, name: str) -> bool: ... + def contents(self) -> Iterator[str]: ... + +class SourceFileLoader(importlib.abc.FileLoader, FileLoader, importlib.abc.SourceLoader, SourceLoader): # type: ignore[misc] # incompatible method arguments in base classes + def set_data(self, path: str, data: ReadableBuffer, *, _mode: int = 0o666) -> None: ... + def path_stats(self, path: str) -> Mapping[str, Any]: ... + +class SourcelessFileLoader(importlib.abc.FileLoader, FileLoader, _LoaderBasics): + def get_code(self, fullname: str) -> types.CodeType | None: ... + def get_source(self, fullname: str) -> None: ... + +class ExtensionFileLoader(FileLoader, _LoaderBasics, importlib.abc.ExecutionLoader): + def __init__(self, name: str, path: str) -> None: ... + def get_filename(self, name: str | None = None) -> str: ... + def get_source(self, fullname: str) -> None: ... + def create_module(self, spec: ModuleSpec) -> types.ModuleType: ... + def exec_module(self, module: types.ModuleType) -> None: ... + def get_code(self, fullname: str) -> None: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + +if sys.version_info >= (3, 11): + class NamespaceLoader(importlib.abc.InspectLoader): + def __init__( + self, name: str, path: MutableSequence[str], path_finder: Callable[[str, tuple[str, ...]], ModuleSpec] + ) -> None: ... + def is_package(self, fullname: str) -> Literal[True]: ... + def get_source(self, fullname: str) -> Literal[""]: ... + def get_code(self, fullname: str) -> types.CodeType: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def exec_module(self, module: types.ModuleType) -> None: ... + @deprecated("load_module() is deprecated; use exec_module() instead") + def load_module(self, fullname: str) -> types.ModuleType: ... + def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... + if sys.version_info < (3, 12): + @staticmethod + @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") + def module_repr(module: types.ModuleType) -> str: ... + + _NamespaceLoader = NamespaceLoader +else: + class _NamespaceLoader: + def __init__( + self, name: str, path: MutableSequence[str], path_finder: Callable[[str, tuple[str, ...]], ModuleSpec] + ) -> None: ... + def is_package(self, fullname: str) -> Literal[True]: ... + def get_source(self, fullname: str) -> Literal[""]: ... + def get_code(self, fullname: str) -> types.CodeType: ... + def create_module(self, spec: ModuleSpec) -> None: ... + def exec_module(self, module: types.ModuleType) -> None: ... + @deprecated("load_module() is deprecated; use exec_module() instead") + def load_module(self, fullname: str) -> types.ModuleType: ... + if sys.version_info >= (3, 10): + @staticmethod + @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") + def module_repr(module: types.ModuleType) -> str: ... + def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... + else: + @classmethod + @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") + def module_repr(cls, module: types.ModuleType) -> str: ... + +if sys.version_info >= (3, 13): + class AppleFrameworkLoader(ExtensionFileLoader, importlib.abc.ExecutionLoader): ... diff --git a/mypy/typeshed/stdlib/_gdbm.pyi b/mypy/typeshed/stdlib/_gdbm.pyi new file mode 100644 index 0000000000000..1d1d541f54770 --- /dev/null +++ b/mypy/typeshed/stdlib/_gdbm.pyi @@ -0,0 +1,47 @@ +import sys +from _typeshed import ReadOnlyBuffer, StrOrBytesPath +from types import TracebackType +from typing import TypeVar, overload +from typing_extensions import Self, TypeAlias + +if sys.platform != "win32": + _T = TypeVar("_T") + _KeyType: TypeAlias = str | ReadOnlyBuffer + _ValueType: TypeAlias = str | ReadOnlyBuffer + + open_flags: str + + class error(OSError): ... + # Actual typename gdbm, not exposed by the implementation + class _gdbm: + def firstkey(self) -> bytes | None: ... + def nextkey(self, key: _KeyType) -> bytes | None: ... + def reorganize(self) -> None: ... + def sync(self) -> None: ... + def close(self) -> None: ... + if sys.version_info >= (3, 13): + def clear(self) -> None: ... + + def __getitem__(self, item: _KeyType) -> bytes: ... + def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... + def __delitem__(self, key: _KeyType) -> None: ... + def __contains__(self, key: _KeyType) -> bool: ... + def __len__(self) -> int: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + @overload + def get(self, k: _KeyType) -> bytes | None: ... + @overload + def get(self, k: _KeyType, default: _T) -> bytes | _T: ... + def keys(self) -> list[bytes]: ... + def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... + # Don't exist at runtime + __new__: None # type: ignore[assignment] + __init__: None # type: ignore[assignment] + + if sys.version_info >= (3, 11): + def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... + else: + def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... diff --git a/mypy/typeshed/stdlib/_hashlib.pyi b/mypy/typeshed/stdlib/_hashlib.pyi new file mode 100644 index 0000000000000..5cf85e4cacaae --- /dev/null +++ b/mypy/typeshed/stdlib/_hashlib.pyi @@ -0,0 +1,80 @@ +import sys +from _typeshed import ReadableBuffer +from collections.abc import Callable +from types import ModuleType +from typing import AnyStr, final, overload +from typing_extensions import Self, TypeAlias + +_DigestMod: TypeAlias = str | Callable[[], HASH] | ModuleType | None + +openssl_md_meth_names: frozenset[str] + +class HASH: + @property + def digest_size(self) -> int: ... + @property + def block_size(self) -> int: ... + @property + def name(self) -> str: ... + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, obj: ReadableBuffer, /) -> None: ... + +if sys.version_info >= (3, 10): + class UnsupportedDigestmodError(ValueError): ... + +if sys.version_info >= (3, 9): + class HASHXOF(HASH): + def digest(self, length: int) -> bytes: ... # type: ignore[override] + def hexdigest(self, length: int) -> str: ... # type: ignore[override] + + @final + class HMAC: + @property + def digest_size(self) -> int: ... + @property + def block_size(self) -> int: ... + @property + def name(self) -> str: ... + def copy(self) -> Self: ... + def digest(self) -> bytes: ... + def hexdigest(self) -> str: ... + def update(self, msg: ReadableBuffer) -> None: ... + + @overload + def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ... + @overload + def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... + def get_fips_mode() -> int: ... + def hmac_new(key: bytes | bytearray, msg: ReadableBuffer = b"", digestmod: _DigestMod = None) -> HMAC: ... + def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ... + def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ... + def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ... + +else: + def new(name: str, string: ReadableBuffer = b"") -> HASH: ... + def openssl_md5(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha1(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha224(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha256(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha384(string: ReadableBuffer = b"") -> HASH: ... + def openssl_sha512(string: ReadableBuffer = b"") -> HASH: ... + +def hmac_digest(key: bytes | bytearray, msg: ReadableBuffer, digest: str) -> bytes: ... +def pbkdf2_hmac( + hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None +) -> bytes: ... +def scrypt( + password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64 +) -> bytes: ... diff --git a/mypy/typeshed/stdlib/_interpchannels.pyi b/mypy/typeshed/stdlib/_interpchannels.pyi new file mode 100644 index 0000000000000..c03496044df06 --- /dev/null +++ b/mypy/typeshed/stdlib/_interpchannels.pyi @@ -0,0 +1,86 @@ +from _typeshed import structseq +from typing import Any, Final, Literal, SupportsIndex, final +from typing_extensions import Buffer, Self + +class ChannelError(RuntimeError): ... +class ChannelClosedError(ChannelError): ... +class ChannelEmptyError(ChannelError): ... +class ChannelNotEmptyError(ChannelError): ... +class ChannelNotFoundError(ChannelError): ... + +# Mark as final, since instantiating ChannelID is not supported. +@final +class ChannelID: + @property + def end(self) -> Literal["send", "recv", "both"]: ... + @property + def send(self) -> Self: ... + @property + def recv(self) -> Self: ... + def __eq__(self, other: object) -> bool: ... + def __ge__(self, other: ChannelID) -> bool: ... + def __gt__(self, other: ChannelID) -> bool: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __le__(self, other: ChannelID) -> bool: ... + def __lt__(self, other: ChannelID) -> bool: ... + def __ne__(self, other: object) -> bool: ... + +@final +class ChannelInfo(structseq[int], tuple[bool, bool, bool, int, int, int, int, int]): + __match_args__: Final = ( + "open", + "closing", + "closed", + "count", + "num_interp_send", + "num_interp_send_released", + "num_interp_recv", + "num_interp_recv_released", + ) + @property + def open(self) -> bool: ... + @property + def closing(self) -> bool: ... + @property + def closed(self) -> bool: ... + @property + def count(self) -> int: ... # type: ignore[override] + @property + def num_interp_send(self) -> int: ... + @property + def num_interp_send_released(self) -> int: ... + @property + def num_interp_recv(self) -> int: ... + @property + def num_interp_recv_released(self) -> int: ... + @property + def num_interp_both(self) -> int: ... + @property + def num_interp_both_recv_released(self) -> int: ... + @property + def num_interp_both_send_released(self) -> int: ... + @property + def num_interp_both_released(self) -> int: ... + @property + def recv_associated(self) -> bool: ... + @property + def recv_released(self) -> bool: ... + @property + def send_associated(self) -> bool: ... + @property + def send_released(self) -> bool: ... + +def create(unboundop: Literal[1, 2, 3]) -> ChannelID: ... +def destroy(cid: SupportsIndex) -> None: ... +def list_all() -> list[ChannelID]: ... +def list_interpreters(cid: SupportsIndex, *, send: bool) -> list[int]: ... +def send(cid: SupportsIndex, obj: object, *, blocking: bool = True, timeout: float | None = None) -> None: ... +def send_buffer(cid: SupportsIndex, obj: Buffer, *, blocking: bool = True, timeout: float | None = None) -> None: ... +def recv(cid: SupportsIndex, default: object = ...) -> tuple[Any, Literal[1, 2, 3]]: ... +def close(cid: SupportsIndex, *, send: bool = False, recv: bool = False) -> None: ... +def get_count(cid: SupportsIndex) -> int: ... +def get_info(cid: SupportsIndex) -> ChannelInfo: ... +def get_channel_defaults(cid: SupportsIndex) -> Literal[1, 2, 3]: ... +def release(cid: SupportsIndex, *, send: bool = False, recv: bool = False, force: bool = False) -> None: ... diff --git a/mypy/typeshed/stdlib/_interpqueues.pyi b/mypy/typeshed/stdlib/_interpqueues.pyi new file mode 100644 index 0000000000000..db5e4cff5068a --- /dev/null +++ b/mypy/typeshed/stdlib/_interpqueues.pyi @@ -0,0 +1,16 @@ +from typing import Any, SupportsIndex + +class QueueError(RuntimeError): ... +class QueueNotFoundError(QueueError): ... + +def bind(qid: SupportsIndex) -> None: ... +def create(maxsize: SupportsIndex, fmt: SupportsIndex) -> int: ... +def destroy(qid: SupportsIndex) -> None: ... +def get(qid: SupportsIndex) -> tuple[Any, int]: ... +def get_count(qid: SupportsIndex) -> int: ... +def get_maxsize(qid: SupportsIndex) -> int: ... +def get_queue_defaults(qid: SupportsIndex) -> tuple[int]: ... +def is_full(qid: SupportsIndex) -> bool: ... +def list_all() -> list[tuple[int, int]]: ... +def put(qid: SupportsIndex, obj: Any, fmt: SupportsIndex) -> None: ... +def release(qid: SupportsIndex) -> None: ... diff --git a/mypy/typeshed/stdlib/_interpreters.pyi b/mypy/typeshed/stdlib/_interpreters.pyi new file mode 100644 index 0000000000000..a57ef13c6d0f8 --- /dev/null +++ b/mypy/typeshed/stdlib/_interpreters.pyi @@ -0,0 +1,50 @@ +import types +from collections.abc import Callable, Mapping +from typing import Final, Literal, SupportsIndex +from typing_extensions import TypeAlias + +_Configs: TypeAlias = Literal["default", "isolated", "legacy", "empty", ""] + +class InterpreterError(Exception): ... +class InterpreterNotFoundError(InterpreterError): ... +class NotShareableError(ValueError): ... + +class CrossInterpreterBufferView: + def __buffer__(self, flags: int, /) -> memoryview: ... + +def new_config(name: _Configs = "isolated", /, **overides: object) -> types.SimpleNamespace: ... +def create(config: types.SimpleNamespace | _Configs | None = "isolated", *, reqrefs: bool = False) -> int: ... +def destroy(id: SupportsIndex, *, restrict: bool = False) -> None: ... +def list_all(*, require_ready: bool) -> list[tuple[int, int]]: ... +def get_current() -> tuple[int, int]: ... +def get_main() -> tuple[int, int]: ... +def is_running(id: SupportsIndex, *, restrict: bool = False) -> bool: ... +def get_config(id: SupportsIndex, *, restrict: bool = False) -> types.SimpleNamespace: ... +def whence(id: SupportsIndex) -> int: ... +def exec(id: SupportsIndex, code: str, shared: bool | None = None, *, restrict: bool = False) -> None: ... +def call( + id: SupportsIndex, + callable: Callable[..., object], + args: tuple[object, ...] | None = None, + kwargs: dict[str, object] | None = None, + *, + restrict: bool = False, +) -> object: ... +def run_string( + id: SupportsIndex, script: str | types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False +) -> None: ... +def run_func( + id: SupportsIndex, func: types.CodeType | Callable[[], object], shared: bool | None = None, *, restrict: bool = False +) -> None: ... +def set___main___attrs(id: SupportsIndex, updates: Mapping[str, object], *, restrict: bool = False) -> None: ... +def incref(id: SupportsIndex, *, implieslink: bool = False, restrict: bool = False) -> None: ... +def decref(id: SupportsIndex, *, restrict: bool = False) -> None: ... +def is_shareable(obj: object) -> bool: ... +def capture_exception(exc: BaseException | None = None) -> types.SimpleNamespace: ... + +WHENCE_UNKNOWN: Final = 0 +WHENCE_RUNTIME: Final = 1 +WHENCE_LEGACY_CAPI: Final = 2 +WHENCE_CAPI: Final = 3 +WHENCE_XI: Final = 4 +WHENCE_STDLIB: Final = 5 diff --git a/mypy/typeshed/stdlib/_io.pyi b/mypy/typeshed/stdlib/_io.pyi new file mode 100644 index 0000000000000..284d99f92b60d --- /dev/null +++ b/mypy/typeshed/stdlib/_io.pyi @@ -0,0 +1,216 @@ +import builtins +import codecs +import sys +from _typeshed import FileDescriptorOrPath, MaybeNone, ReadableBuffer, WriteableBuffer +from collections.abc import Callable, Iterable, Iterator +from io import BufferedIOBase, RawIOBase, TextIOBase, UnsupportedOperation as UnsupportedOperation +from os import _Opener +from types import TracebackType +from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only +from typing_extensions import Self + +_T = TypeVar("_T") + +DEFAULT_BUFFER_SIZE: Final = 8192 + +open = builtins.open + +def open_code(path: str) -> IO[bytes]: ... + +BlockingIOError = builtins.BlockingIOError + +class _IOBase: + def __iter__(self) -> Iterator[bytes]: ... + def __next__(self) -> bytes: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + def close(self) -> None: ... + def fileno(self) -> int: ... + def flush(self) -> None: ... + def isatty(self) -> bool: ... + def readable(self) -> bool: ... + read: Callable[..., Any] + def readlines(self, hint: int = -1, /) -> list[bytes]: ... + def seek(self, offset: int, whence: int = 0, /) -> int: ... + def seekable(self) -> bool: ... + def tell(self) -> int: ... + def truncate(self, size: int | None = None, /) -> int: ... + def writable(self) -> bool: ... + write: Callable[..., Any] + def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ... + def readline(self, size: int | None = -1, /) -> bytes: ... + def __del__(self) -> None: ... + @property + def closed(self) -> bool: ... + def _checkClosed(self) -> None: ... # undocumented + +class _RawIOBase(_IOBase): + def readall(self) -> bytes: ... + # The following methods can return None if the file is in non-blocking mode + # and no data is available. + def readinto(self, buffer: WriteableBuffer, /) -> int | MaybeNone: ... + def write(self, b: ReadableBuffer, /) -> int | MaybeNone: ... + def read(self, size: int = -1, /) -> bytes | MaybeNone: ... + +class _BufferedIOBase(_IOBase): + def detach(self) -> RawIOBase: ... + def readinto(self, buffer: WriteableBuffer, /) -> int: ... + def write(self, buffer: ReadableBuffer, /) -> int: ... + def readinto1(self, buffer: WriteableBuffer, /) -> int: ... + def read(self, size: int | None = -1, /) -> bytes: ... + def read1(self, size: int = -1, /) -> bytes: ... + +class FileIO(RawIOBase, _RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes + mode: str + # The type of "name" equals the argument passed in to the constructor, + # but that can make FileIO incompatible with other I/O types that assume + # "name" is a str. In the future, making FileIO generic might help. + name: Any + def __init__( + self, file: FileDescriptorOrPath, mode: str = "r", closefd: bool = True, opener: _Opener | None = None + ) -> None: ... + @property + def closefd(self) -> bool: ... + def seek(self, pos: int, whence: int = 0, /) -> int: ... + def read(self, size: int | None = -1, /) -> bytes | MaybeNone: ... + +class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + def __init__(self, initial_bytes: ReadableBuffer = b"") -> None: ... + # 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 getbuffer(self) -> memoryview: ... + def read1(self, size: int | None = -1, /) -> bytes: ... + def readlines(self, size: int | None = None, /) -> list[bytes]: ... + def seek(self, pos: int, whence: int = 0, /) -> int: ... + +class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + raw: RawIOBase + def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... + def peek(self, size: int = 0, /) -> bytes: ... + def seek(self, target: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... + +class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes + raw: RawIOBase + def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... + def write(self, buffer: ReadableBuffer, /) -> int: ... + def seek(self, target: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... + +class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes + mode: str + name: Any + raw: RawIOBase + def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... + def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this + def peek(self, size: int = 0, /) -> bytes: ... + def truncate(self, pos: int | None = None, /) -> int: ... + +class BufferedRWPair(BufferedIOBase, _BufferedIOBase): + def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = 8192) -> None: ... + def peek(self, size: int = 0, /) -> bytes: ... + +class _TextIOBase(_IOBase): + encoding: str + errors: str | None + newlines: str | tuple[str, ...] | None + def __iter__(self) -> Iterator[str]: ... # type: ignore[override] + def __next__(self) -> str: ... # type: ignore[override] + def detach(self) -> BinaryIO: ... + def write(self, s: str, /) -> int: ... + def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] + def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] + def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] + def read(self, size: int | None = -1, /) -> str: ... + +@type_check_only +class _WrappedBuffer(Protocol): + # "name" is wrapped by TextIOWrapper. Its type is inconsistent between + # the various I/O types, see the comments on TextIOWrapper.name and + # TextIO.name. + @property + def name(self) -> Any: ... + @property + def closed(self) -> bool: ... + def read(self, size: int = ..., /) -> ReadableBuffer: ... + # Optional: def read1(self, size: int, /) -> ReadableBuffer: ... + def write(self, b: bytes, /) -> object: ... + def flush(self) -> object: ... + def close(self) -> object: ... + def seekable(self) -> bool: ... + def readable(self) -> bool: ... + def writable(self) -> bool: ... + def truncate(self, size: int, /) -> int: ... + def fileno(self) -> int: ... + def isatty(self) -> bool: ... + # Optional: Only needs to be present if seekable() returns True. + # def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ... + # def tell(self) -> int: ... + +_BufferT_co = TypeVar("_BufferT_co", bound=_WrappedBuffer, default=_WrappedBuffer, covariant=True) + +class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]): # type: ignore[misc] # incompatible definitions of write in the base classes + def __init__( + self, + buffer: _BufferT_co, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool = False, + write_through: bool = False, + ) -> None: ... + # Equals the "buffer" argument passed in to the constructor. + @property + def buffer(self) -> _BufferT_co: ... # type: ignore[override] + @property + def line_buffering(self) -> bool: ... + @property + def write_through(self) -> bool: ... + def reconfigure( + self, + *, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + line_buffering: bool | None = None, + write_through: bool | None = None, + ) -> None: ... + def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] + # Equals the "buffer" argument passed in to the constructor. + def detach(self) -> _BufferT_co: ... # type: ignore[override] + # TextIOWrapper's version of seek only supports a limited subset of + # operations. + def seek(self, cookie: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... + +class StringIO(TextIOBase, _TextIOBase, TextIO): # type: ignore[misc] # incompatible definitions of write in the base classes + def __init__(self, initial_value: str | None = "", newline: str | None = "\n") -> None: ... + # 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) -> str: ... + @property + def line_buffering(self) -> bool: ... + def seek(self, pos: int, whence: int = 0, /) -> int: ... + def truncate(self, pos: int | None = None, /) -> int: ... + +class IncrementalNewlineDecoder: + def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = "strict") -> None: ... + def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ... + @property + def newlines(self) -> str | tuple[str, ...] | None: ... + def getstate(self) -> tuple[bytes, int]: ... + def reset(self) -> None: ... + def setstate(self, state: tuple[bytes, int], /) -> None: ... + +if sys.version_info >= (3, 10): + @overload + def text_encoding(encoding: None, stacklevel: int = 2, /) -> Literal["locale", "utf-8"]: ... + @overload + def text_encoding(encoding: _T, stacklevel: int = 2, /) -> _T: ... diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi index 069fb6eac4bf5..e1c7c52ca3b10 100644 --- a/mypy/typeshed/stdlib/_json.pyi +++ b/mypy/typeshed/stdlib/_json.pyi @@ -45,5 +45,6 @@ class make_scanner: def __init__(self, context: make_scanner) -> None: ... def __call__(self, string: str, index: int) -> tuple[Any, int]: ... +def encode_basestring(s: str, /) -> str: ... def encode_basestring_ascii(s: str, /) -> str: ... def scanstring(string: str, end: int, strict: bool = ...) -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/_locale.pyi b/mypy/typeshed/stdlib/_locale.pyi index 0825e12034f4a..ccce7a0d9d70f 100644 --- a/mypy/typeshed/stdlib/_locale.pyi +++ b/mypy/typeshed/stdlib/_locale.pyi @@ -1,17 +1,38 @@ import sys from _typeshed import StrPath -from collections.abc import Mapping +from typing import Final, Literal, TypedDict, type_check_only -LC_CTYPE: int -LC_COLLATE: int -LC_TIME: int -LC_MONETARY: int -LC_NUMERIC: int -LC_ALL: int -CHAR_MAX: int +@type_check_only +class _LocaleConv(TypedDict): + decimal_point: str + grouping: list[int] + thousands_sep: str + int_curr_symbol: str + currency_symbol: str + p_cs_precedes: Literal[0, 1, 127] + n_cs_precedes: Literal[0, 1, 127] + p_sep_by_space: Literal[0, 1, 127] + n_sep_by_space: Literal[0, 1, 127] + mon_decimal_point: str + frac_digits: int + int_frac_digits: int + mon_thousands_sep: str + mon_grouping: list[int] + positive_sign: str + negative_sign: str + p_sign_posn: Literal[0, 1, 2, 3, 4, 127] + n_sign_posn: Literal[0, 1, 2, 3, 4, 127] + +LC_CTYPE: Final[int] +LC_COLLATE: Final[int] +LC_TIME: Final[int] +LC_MONETARY: Final[int] +LC_NUMERIC: Final[int] +LC_ALL: Final[int] +CHAR_MAX: Final = 127 def setlocale(category: int, locale: str | None = None, /) -> str: ... -def localeconv() -> Mapping[str, int | str | list[int]]: ... +def localeconv() -> _LocaleConv: ... if sys.version_info >= (3, 11): def getencoding() -> str: ... @@ -25,67 +46,67 @@ def strxfrm(string: str, /) -> str: ... if sys.platform != "win32": LC_MESSAGES: int - ABDAY_1: int - ABDAY_2: int - ABDAY_3: int - ABDAY_4: int - ABDAY_5: int - ABDAY_6: int - ABDAY_7: int + ABDAY_1: Final[int] + ABDAY_2: Final[int] + ABDAY_3: Final[int] + ABDAY_4: Final[int] + ABDAY_5: Final[int] + ABDAY_6: Final[int] + ABDAY_7: Final[int] - ABMON_1: int - ABMON_2: int - ABMON_3: int - ABMON_4: int - ABMON_5: int - ABMON_6: int - ABMON_7: int - ABMON_8: int - ABMON_9: int - ABMON_10: int - ABMON_11: int - ABMON_12: int + ABMON_1: Final[int] + ABMON_2: Final[int] + ABMON_3: Final[int] + ABMON_4: Final[int] + ABMON_5: Final[int] + ABMON_6: Final[int] + ABMON_7: Final[int] + ABMON_8: Final[int] + ABMON_9: Final[int] + ABMON_10: Final[int] + ABMON_11: Final[int] + ABMON_12: Final[int] - DAY_1: int - DAY_2: int - DAY_3: int - DAY_4: int - DAY_5: int - DAY_6: int - DAY_7: int + DAY_1: Final[int] + DAY_2: Final[int] + DAY_3: Final[int] + DAY_4: Final[int] + DAY_5: Final[int] + DAY_6: Final[int] + DAY_7: Final[int] - ERA: int - ERA_D_T_FMT: int - ERA_D_FMT: int - ERA_T_FMT: int + ERA: Final[int] + ERA_D_T_FMT: Final[int] + ERA_D_FMT: Final[int] + ERA_T_FMT: Final[int] - MON_1: int - MON_2: int - MON_3: int - MON_4: int - MON_5: int - MON_6: int - MON_7: int - MON_8: int - MON_9: int - MON_10: int - MON_11: int - MON_12: int + MON_1: Final[int] + MON_2: Final[int] + MON_3: Final[int] + MON_4: Final[int] + MON_5: Final[int] + MON_6: Final[int] + MON_7: Final[int] + MON_8: Final[int] + MON_9: Final[int] + MON_10: Final[int] + MON_11: Final[int] + MON_12: Final[int] - CODESET: int - D_T_FMT: int - D_FMT: int - T_FMT: int - T_FMT_AMPM: int - AM_STR: int - PM_STR: int + CODESET: Final[int] + D_T_FMT: Final[int] + D_FMT: Final[int] + T_FMT: Final[int] + T_FMT_AMPM: Final[int] + AM_STR: Final[int] + PM_STR: Final[int] - RADIXCHAR: int - THOUSEP: int - YESEXPR: int - NOEXPR: int - CRNCYSTR: int - ALT_DIGITS: int + RADIXCHAR: Final[int] + THOUSEP: Final[int] + YESEXPR: Final[int] + NOEXPR: Final[int] + CRNCYSTR: Final[int] + ALT_DIGITS: Final[int] def nl_langinfo(key: int, /) -> str: ... diff --git a/mypy/typeshed/stdlib/_lzma.pyi b/mypy/typeshed/stdlib/_lzma.pyi new file mode 100644 index 0000000000000..1f5be78876c61 --- /dev/null +++ b/mypy/typeshed/stdlib/_lzma.pyi @@ -0,0 +1,60 @@ +from _typeshed import ReadableBuffer +from collections.abc import Mapping, Sequence +from typing import Any, Final, final +from typing_extensions import TypeAlias + +_FilterChain: TypeAlias = Sequence[Mapping[str, Any]] + +FORMAT_AUTO: Final = 0 +FORMAT_XZ: Final = 1 +FORMAT_ALONE: Final = 2 +FORMAT_RAW: Final = 3 +CHECK_NONE: Final = 0 +CHECK_CRC32: Final = 1 +CHECK_CRC64: Final = 4 +CHECK_SHA256: Final = 10 +CHECK_ID_MAX: Final = 15 +CHECK_UNKNOWN: Final = 16 +FILTER_LZMA1: int # v big number +FILTER_LZMA2: Final = 33 +FILTER_DELTA: Final = 3 +FILTER_X86: Final = 4 +FILTER_IA64: Final = 6 +FILTER_ARM: Final = 7 +FILTER_ARMTHUMB: Final = 8 +FILTER_SPARC: Final = 9 +FILTER_POWERPC: Final = 5 +MF_HC3: Final = 3 +MF_HC4: Final = 4 +MF_BT2: Final = 18 +MF_BT3: Final = 19 +MF_BT4: Final = 20 +MODE_FAST: Final = 1 +MODE_NORMAL: Final = 2 +PRESET_DEFAULT: Final = 6 +PRESET_EXTREME: int # v big number + +@final +class LZMADecompressor: + def __init__(self, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> None: ... + def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... + @property + def check(self) -> int: ... + @property + def eof(self) -> bool: ... + @property + def unused_data(self) -> bytes: ... + @property + def needs_input(self) -> bool: ... + +@final +class LZMACompressor: + def __init__( + self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... + ) -> None: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... + def flush(self) -> bytes: ... + +class LZMAError(Exception): ... + +def is_check_supported(check_id: int, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/_multibytecodec.pyi b/mypy/typeshed/stdlib/_multibytecodec.pyi new file mode 100644 index 0000000000000..7e408f2aa30e1 --- /dev/null +++ b/mypy/typeshed/stdlib/_multibytecodec.pyi @@ -0,0 +1,44 @@ +from _typeshed import ReadableBuffer +from codecs import _ReadableStream, _WritableStream +from collections.abc import Iterable +from typing import final, type_check_only + +# This class is not exposed. It calls itself _multibytecodec.MultibyteCodec. +@final +@type_check_only +class _MultibyteCodec: + def decode(self, input: ReadableBuffer, errors: str | None = None) -> str: ... + def encode(self, input: str, errors: str | None = None) -> bytes: ... + +class MultibyteIncrementalDecoder: + errors: str + def __init__(self, errors: str = "strict") -> None: ... + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + def getstate(self) -> tuple[bytes, int]: ... + def reset(self) -> None: ... + def setstate(self, state: tuple[bytes, int], /) -> None: ... + +class MultibyteIncrementalEncoder: + errors: str + def __init__(self, errors: str = "strict") -> None: ... + def encode(self, input: str, final: bool = False) -> bytes: ... + def getstate(self) -> int: ... + def reset(self) -> None: ... + def setstate(self, state: int, /) -> None: ... + +class MultibyteStreamReader: + errors: str + stream: _ReadableStream + def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ... + def read(self, sizeobj: int | None = None, /) -> str: ... + def readline(self, sizeobj: int | None = None, /) -> str: ... + def readlines(self, sizehintobj: int | None = None, /) -> list[str]: ... + def reset(self) -> None: ... + +class MultibyteStreamWriter: + errors: str + stream: _WritableStream + def __init__(self, stream: _WritableStream, errors: str = "strict") -> None: ... + def reset(self) -> None: ... + def write(self, strobj: str, /) -> None: ... + def writelines(self, lines: Iterable[str], /) -> None: ... diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index 69ee563b5cf4f..967215d8fa211 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -1,18 +1,16 @@ import sys from _typeshed import SupportsGetItem from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence -from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, SupportsIndex, TypeVar, final, overload -from typing_extensions import ParamSpec, TypeAlias, TypeVarTuple, Unpack +from operator import attrgetter as attrgetter, itemgetter as itemgetter, methodcaller as methodcaller +from typing import Any, AnyStr, Protocol, SupportsAbs, SupportsIndex, TypeVar, overload +from typing_extensions import ParamSpec, TypeAlias, TypeIs _R = TypeVar("_R") _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") _K = TypeVar("_K") _V = TypeVar("_V") _P = ParamSpec("_P") -_Ts = TypeVarTuple("_Ts") # The following protocols return "Any" instead of bool, since the comparison # operators can be overloaded to return an arbitrary object. For example, @@ -92,40 +90,6 @@ def setitem(a: MutableSequence[_T], b: slice, c: Sequence[_T], /) -> None: ... @overload def setitem(a: MutableMapping[_K, _V], b: _K, c: _V, /) -> None: ... def length_hint(obj: object, default: int = 0, /) -> int: ... -@final -class attrgetter(Generic[_T_co]): - @overload - def __new__(cls, attr: str, /) -> attrgetter[Any]: ... - @overload - def __new__(cls, attr: str, attr2: str, /) -> attrgetter[tuple[Any, Any]]: ... - @overload - def __new__(cls, attr: str, attr2: str, attr3: str, /) -> attrgetter[tuple[Any, Any, Any]]: ... - @overload - def __new__(cls, attr: str, attr2: str, attr3: str, attr4: str, /) -> attrgetter[tuple[Any, Any, Any, Any]]: ... - @overload - def __new__(cls, attr: str, /, *attrs: str) -> attrgetter[tuple[Any, ...]]: ... - def __call__(self, obj: Any, /) -> _T_co: ... - -@final -class itemgetter(Generic[_T_co]): - @overload - def __new__(cls, item: _T, /) -> itemgetter[_T]: ... - @overload - def __new__(cls, item1: _T1, item2: _T2, /, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ... - # __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie: - # TypeVar "_KT_contra@SupportsGetItem" is contravariant - # "tuple[int, int]" is incompatible with protocol "SupportsIndex" - # preventing [_T_co, ...] instead of [Any, ...] - # - # A suspected mypy issue prevents using [..., _T] instead of [..., Any] here. - # https://github.com/python/mypy/issues/14032 - def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ... - -@final -class methodcaller: - def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ... - def __call__(self, obj: Any) -> Any: ... - def iadd(a: Any, b: Any, /) -> Any: ... def iand(a: Any, b: Any, /) -> Any: ... def iconcat(a: Any, b: Any, /) -> Any: ... @@ -145,3 +109,7 @@ if sys.version_info >= (3, 11): def call(obj: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... def _compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... + +if sys.version_info >= (3, 14): + def is_none(a: object, /) -> TypeIs[None]: ... + def is_not_none(a: _T | None, /) -> TypeIs[_T]: ... diff --git a/mypy/typeshed/stdlib/_osx_support.pyi b/mypy/typeshed/stdlib/_osx_support.pyi index 64dbdd24fd401..fb00e6986dd06 100644 --- a/mypy/typeshed/stdlib/_osx_support.pyi +++ b/mypy/typeshed/stdlib/_osx_support.pyi @@ -1,5 +1,5 @@ from collections.abc import Iterable, Sequence -from typing import TypeVar +from typing import Final, TypeVar _T = TypeVar("_T") _K = TypeVar("_K") @@ -7,15 +7,15 @@ _V = TypeVar("_V") __all__ = ["compiler_fixup", "customize_config_vars", "customize_compiler", "get_platform_osx"] -_UNIVERSAL_CONFIG_VARS: tuple[str, ...] # undocumented -_COMPILER_CONFIG_VARS: tuple[str, ...] # undocumented -_INITPRE: str # undocumented +_UNIVERSAL_CONFIG_VARS: Final[tuple[str, ...]] # undocumented +_COMPILER_CONFIG_VARS: Final[tuple[str, ...]] # undocumented +_INITPRE: Final[str] # undocumented def _find_executable(executable: str, path: str | None = None) -> str | None: ... # undocumented def _read_output(commandstring: str, capture_stderr: bool = False) -> str | None: ... # undocumented def _find_build_tool(toolname: str) -> str: ... # undocumented -_SYSTEM_VERSION: str | None # undocumented +_SYSTEM_VERSION: Final[str | None] # undocumented def _get_system_version() -> str: ... # undocumented def _remove_original_values(_config_vars: dict[str, str]) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi index 3df56d9a3d03a..df05dcd80be80 100644 --- a/mypy/typeshed/stdlib/_posixsubprocess.pyi +++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi @@ -4,7 +4,6 @@ from collections.abc import Callable, Sequence from typing import SupportsIndex if sys.platform != "win32": - def cloexec_pipe() -> tuple[int, int]: ... def fork_exec( args: Sequence[StrOrBytesPath] | None, executable_list: Sequence[bytes], diff --git a/mypy/typeshed/stdlib/_queue.pyi b/mypy/typeshed/stdlib/_queue.pyi new file mode 100644 index 0000000000000..0d4caea7442ec --- /dev/null +++ b/mypy/typeshed/stdlib/_queue.pyi @@ -0,0 +1,20 @@ +import sys +from typing import Any, Generic, TypeVar + +if sys.version_info >= (3, 9): + from types import GenericAlias + +_T = TypeVar("_T") + +class Empty(Exception): ... + +class SimpleQueue(Generic[_T]): + def __init__(self) -> None: ... + def empty(self) -> bool: ... + def get(self, block: bool = True, timeout: float | None = None) -> _T: ... + def get_nowait(self) -> _T: ... + def put(self, item: _T, block: bool = True, timeout: float | None = None) -> None: ... + def put_nowait(self, item: _T) -> None: ... + def qsize(self) -> int: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index affa8d63ecfab..36bc5c31c646e 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import ReadableBuffer, WriteableBuffer from collections.abc import Iterable +from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout from typing import Any, SupportsIndex, overload -from typing_extensions import TypeAlias +from typing_extensions import CapsuleType, TypeAlias _CMSG: TypeAlias = tuple[int, int, bytes] _CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer] @@ -71,7 +72,8 @@ SO_SNDBUF: int SO_SNDLOWAT: int SO_SNDTIMEO: int SO_TYPE: int -SO_USELOOPBACK: int +if sys.platform != "linux": + SO_USELOOPBACK: int if sys.platform == "win32": SO_EXCLUSIVEADDRUSE: int if sys.platform != "win32": @@ -86,7 +88,10 @@ if sys.platform != "win32" and sys.platform != "darwin": SO_PEERSEC: int SO_PRIORITY: int SO_PROTOCOL: int +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": SO_SETFIB: int +if sys.platform == "linux" and sys.version_info >= (3, 13): + SO_BINDTOIFINDEX: int SOMAXCONN: int @@ -98,27 +103,32 @@ MSG_TRUNC: int MSG_WAITALL: int if sys.platform != "win32": MSG_DONTWAIT: int - MSG_EOF: int MSG_EOR: int MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not if sys.platform != "darwin": - MSG_BCAST: int MSG_ERRQUEUE: int +if sys.platform == "win32": + MSG_BCAST: int MSG_MCAST: int if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG: int MSG_CMSG_CLOEXEC: int MSG_CONFIRM: int - MSG_ETAG: int MSG_FASTOPEN: int MSG_MORE: int +if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF: int +if sys.platform != "win32" and sys.platform != "linux" and sys.platform != "darwin": MSG_NOTIFICATION: int + MSG_BTAG: int # Not FreeBSD either + MSG_ETAG: int # Not FreeBSD either SOL_IP: int SOL_SOCKET: int SOL_TCP: int SOL_UDP: int if sys.platform != "win32" and sys.platform != "darwin": + # Defined in socket.h for Linux, but these aren't always present for + # some reason. SOL_ATALK: int SOL_AX25: int SOL_HCI: int @@ -127,10 +137,11 @@ if sys.platform != "win32" and sys.platform != "darwin": SOL_ROSE: int if sys.platform != "win32": - SCM_CREDS: int SCM_RIGHTS: int if sys.platform != "win32" and sys.platform != "darwin": SCM_CREDENTIALS: int +if sys.platform != "win32" and sys.platform != "linux": + SCM_CREDS: int IPPROTO_ICMP: int IPPROTO_IP: int @@ -142,21 +153,22 @@ IPPROTO_DSTOPTS: int IPPROTO_EGP: int IPPROTO_ESP: int IPPROTO_FRAGMENT: int -IPPROTO_GGP: int IPPROTO_HOPOPTS: int IPPROTO_ICMPV6: int IPPROTO_IDP: int IPPROTO_IGMP: int -IPPROTO_IPV4: int IPPROTO_IPV6: int -IPPROTO_MAX: int -IPPROTO_ND: int IPPROTO_NONE: int IPPROTO_PIM: int IPPROTO_PUP: int IPPROTO_ROUTING: int IPPROTO_SCTP: int -if sys.platform != "darwin": +if sys.platform != "linux": + IPPROTO_GGP: int + IPPROTO_IPV4: int + IPPROTO_MAX: int + IPPROTO_ND: int +if sys.platform == "win32": IPPROTO_CBT: int IPPROTO_ICLFXBM: int IPPROTO_IGP: int @@ -165,18 +177,19 @@ if sys.platform != "darwin": IPPROTO_RDP: int IPPROTO_ST: int if sys.platform != "win32": - IPPROTO_EON: int IPPROTO_GRE: int - IPPROTO_HELLO: int - IPPROTO_IPCOMP: int IPPROTO_IPIP: int IPPROTO_RSVP: int IPPROTO_TP: int +if sys.platform != "win32" and sys.platform != "linux": + IPPROTO_EON: int + IPPROTO_HELLO: int + IPPROTO_IPCOMP: int IPPROTO_XTP: int -if sys.platform != "win32" and sys.platform != "darwin": - IPPROTO_BIP: int - IPPROTO_MOBILE: int - IPPROTO_VRRP: int +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + IPPROTO_BIP: int # Not FreeBSD either + IPPROTO_MOBILE: int # Not FreeBSD either + IPPROTO_VRRP: int # Not FreeBSD either if sys.version_info >= (3, 9) and sys.platform == "linux": # Availability: Linux >= 2.6.20, FreeBSD >= 10.1 IPPROTO_UDPLITE: int @@ -201,11 +214,10 @@ IP_MULTICAST_IF: int IP_MULTICAST_LOOP: int IP_MULTICAST_TTL: int IP_OPTIONS: int -IP_RECVDSTADDR: int +if sys.platform != "linux": + IP_RECVDSTADDR: int if sys.version_info >= (3, 10): IP_RECVTOS: int -elif sys.platform != "win32" and sys.platform != "darwin": - IP_RECVTOS: int IP_TOS: int IP_TTL: int if sys.platform != "win32": @@ -217,6 +229,7 @@ if sys.platform != "win32": IP_RETOPTS: int if sys.platform != "win32" and sys.platform != "darwin": IP_TRANSPARENT: int +if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11): IP_BIND_ADDRESS_NO_PORT: int if sys.version_info >= (3, 12): IP_ADD_SOURCE_MEMBERSHIP: int @@ -254,6 +267,9 @@ if sys.platform != "win32": IPV6_RECVPATHMTU: int IPV6_RECVPKTINFO: int IPV6_RTHDRDSTOPTS: int + +if sys.platform != "win32" and sys.platform != "linux": + if sys.version_info >= (3, 9) or sys.platform != "darwin": IPV6_USE_MIN_MTU: int EAI_AGAIN: int @@ -267,11 +283,12 @@ EAI_SERVICE: int EAI_SOCKTYPE: int if sys.platform != "win32": EAI_ADDRFAMILY: int + EAI_OVERFLOW: int + EAI_SYSTEM: int +if sys.platform != "win32" and sys.platform != "linux": EAI_BADHINTS: int EAI_MAX: int - EAI_OVERFLOW: int EAI_PROTOCOL: int - EAI_SYSTEM: int AI_ADDRCONFIG: int AI_ALL: int @@ -280,7 +297,7 @@ AI_NUMERICHOST: int AI_NUMERICSERV: int AI_PASSIVE: int AI_V4MAPPED: int -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": AI_DEFAULT: int AI_MASK: int AI_V4MAPPED_CFG: int @@ -292,6 +309,8 @@ NI_NAMEREQD: int NI_NOFQDN: int NI_NUMERICHOST: int NI_NUMERICSERV: int +if sys.platform == "linux" and sys.version_info >= (3, 13): + NI_IDN: int TCP_FASTOPEN: int TCP_KEEPCNT: int @@ -317,6 +336,27 @@ if sys.platform != "win32" and sys.platform != "darwin": TCP_SYNCNT: int TCP_USER_TIMEOUT: int TCP_WINDOW_CLAMP: int +if sys.platform == "linux" and sys.version_info >= (3, 12): + TCP_CC_INFO: int + TCP_FASTOPEN_CONNECT: int + TCP_FASTOPEN_KEY: int + TCP_FASTOPEN_NO_COOKIE: int + TCP_INQ: int + TCP_MD5SIG: int + TCP_MD5SIG_EXT: int + TCP_QUEUE_SEQ: int + TCP_REPAIR: int + TCP_REPAIR_OPTIONS: int + TCP_REPAIR_QUEUE: int + TCP_REPAIR_WINDOW: int + TCP_SAVED_SYN: int + TCP_SAVE_SYN: int + TCP_THIN_DUPACK: int + TCP_THIN_LINEAR_TIMEOUTS: int + TCP_TIMESTAMP: int + TCP_TX_DELAY: int + TCP_ULP: int + TCP_ZEROCOPY_RECEIVE: int # -------------------- # Specifically documented constants @@ -333,12 +373,13 @@ if sys.platform == "linux": CAN_ERR_FLAG: int CAN_ERR_MASK: int CAN_RAW: int - CAN_RAW_ERR_FILTER: int CAN_RAW_FILTER: int CAN_RAW_LOOPBACK: int CAN_RAW_RECV_OWN_MSGS: int CAN_RTR_FLAG: int CAN_SFF_MASK: int + if sys.version_info < (3, 11): + CAN_RAW_ERR_FILTER: int if sys.platform == "linux": # Availability: Linux >= 2.6.25 @@ -436,12 +477,13 @@ if sys.platform == "linux": AF_RDS: int PF_RDS: int SOL_RDS: int + # These are present in include/linux/rds.h but don't always show up + # here. RDS_CANCEL_SENT_TO: int RDS_CMSG_RDMA_ARGS: int RDS_CMSG_RDMA_DEST: int RDS_CMSG_RDMA_MAP: int RDS_CMSG_RDMA_STATUS: int - RDS_CMSG_RDMA_UPDATE: int RDS_CONG_MONITOR: int RDS_FREE_MR: int RDS_GET_MR: int @@ -455,6 +497,10 @@ if sys.platform == "linux": RDS_RDMA_USE_ONCE: int RDS_RECVERR: int + # This is supported by CPython but doesn't seem to be a real thing. + # The closest existing constant in rds.h is RDS_CMSG_CONG_UPDATE + # RDS_CMSG_RDMA_UPDATE: int + if sys.platform == "win32": SIO_RCVALL: int SIO_KEEPALIVE_VALS: int @@ -521,16 +567,17 @@ if sys.platform == "linux": if sys.platform != "win32" or sys.version_info >= (3, 9): # Documented as only available on BSD, macOS, but empirically sometimes # available on Windows - AF_LINK: int + if sys.platform != "linux": + AF_LINK: int has_ipv6: bool -if sys.platform != "darwin": +if sys.platform != "darwin" and sys.platform != "linux": if sys.platform != "win32" or sys.version_info >= (3, 9): BDADDR_ANY: str BDADDR_LOCAL: str -if sys.platform != "win32" and sys.platform != "darwin": +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": HCI_FILTER: int # not in NetBSD or DragonFlyBSD HCI_TIME_STAMP: int # not in FreeBSD, NetBSD, or DragonFlyBSD HCI_DATA_DIR: int # not in FreeBSD, NetBSD, or DragonFlyBSD @@ -579,36 +626,37 @@ if sys.version_info >= (3, 12): if sys.platform == "linux": # Netlink is defined by Linux AF_NETLINK: int - NETLINK_ARPD: int NETLINK_CRYPTO: int NETLINK_DNRTMSG: int NETLINK_FIREWALL: int NETLINK_IP6_FW: int NETLINK_NFLOG: int - NETLINK_ROUTE6: int NETLINK_ROUTE: int - NETLINK_SKIP: int - NETLINK_TAPBASE: int - NETLINK_TCPDIAG: int NETLINK_USERSOCK: int - NETLINK_W1: int NETLINK_XFRM: int + # Technically still supported by CPython + # NETLINK_ARPD: int # linux 2.0 to 2.6.12 (EOL August 2005) + # NETLINK_ROUTE6: int # linux 2.2 to 2.6.12 (EOL August 2005) + # NETLINK_SKIP: int # linux 2.0 to 2.6.12 (EOL August 2005) + # NETLINK_TAPBASE: int # linux 2.2 to 2.6.12 (EOL August 2005) + # NETLINK_TCPDIAG: int # linux 2.6.0 to 2.6.13 (EOL December 2005) + # NETLINK_W1: int # linux 2.6.13 to 2.6.17 (EOL October 2006) if sys.platform == "darwin": PF_SYSTEM: int SYSPROTO_CONTROL: int -if sys.platform != "darwin": +if sys.platform != "darwin" and sys.platform != "linux": if sys.version_info >= (3, 9) or sys.platform != "win32": AF_BLUETOOTH: int -if sys.platform != "win32" and sys.platform != "darwin": +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": # Linux and some BSD support is explicit in the docs # Windows and macOS do not support in practice BTPROTO_HCI: int BTPROTO_L2CAP: int BTPROTO_SCO: int # not in FreeBSD -if sys.platform != "darwin": +if sys.platform != "darwin" and sys.platform != "linux": if sys.version_info >= (3, 9) or sys.platform != "win32": BTPROTO_RFCOMM: int @@ -635,13 +683,14 @@ AF_SNA: int if sys.platform != "win32": AF_ROUTE: int + +if sys.platform == "darwin": AF_SYSTEM: int if sys.platform != "darwin": AF_IRDA: int if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5: int AF_ASH: int AF_ATMPVC: int AF_ATMSVC: int @@ -660,24 +709,14 @@ if sys.platform != "win32" and sys.platform != "darwin": # Miscellaneous undocumented -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": LOCAL_PEERCRED: int if sys.platform != "win32" and sys.platform != "darwin": + # Defined in linux socket.h, but this isn't always present for + # some reason. IPX_TYPE: int -# ===== Exceptions ===== - -error = OSError - -class herror(error): ... -class gaierror(error): ... - -if sys.version_info >= (3, 10): - timeout = TimeoutError -else: - class timeout(error): ... - # ===== Classes ===== class socket: @@ -687,8 +726,9 @@ class socket: def type(self) -> int: ... @property def proto(self) -> int: ... + # F811: "Redefinition of unused `timeout`" @property - def timeout(self) -> float | None: ... + def timeout(self) -> float | None: ... # noqa: F811 if sys.platform == "win32": def __init__( self, family: int = ..., type: int = ..., proto: int = ..., fileno: SupportsIndex | bytes | None = ... @@ -788,7 +828,9 @@ def inet_ntoa(packed_ip: ReadableBuffer, /) -> str: ... def inet_pton(address_family: int, ip_string: str, /) -> bytes: ... def inet_ntop(address_family: int, packed_ip: ReadableBuffer, /) -> str: ... def getdefaulttimeout() -> float | None: ... -def setdefaulttimeout(timeout: float | None, /) -> None: ... + +# F811: "Redefinition of unused `timeout`" +def setdefaulttimeout(timeout: float | None, /) -> None: ... # noqa: F811 if sys.platform != "win32": def sethostname(name: str, /) -> None: ... @@ -800,4 +842,4 @@ def if_nameindex() -> list[tuple[int, str]]: ... def if_nametoindex(oname: str, /) -> int: ... def if_indextoname(index: int, /) -> str: ... -CAPI: object +CAPI: CapsuleType diff --git a/mypy/typeshed/stdlib/_sqlite3.pyi b/mypy/typeshed/stdlib/_sqlite3.pyi new file mode 100644 index 0000000000000..6f06542c1ba71 --- /dev/null +++ b/mypy/typeshed/stdlib/_sqlite3.pyi @@ -0,0 +1,312 @@ +import sys +from _typeshed import ReadableBuffer, StrOrBytesPath +from collections.abc import Callable +from sqlite3 import ( + Connection as Connection, + Cursor as Cursor, + DatabaseError as DatabaseError, + DataError as DataError, + Error as Error, + IntegrityError as IntegrityError, + InterfaceError as InterfaceError, + InternalError as InternalError, + NotSupportedError as NotSupportedError, + OperationalError as OperationalError, + PrepareProtocol as PrepareProtocol, + ProgrammingError as ProgrammingError, + Row as Row, + Warning as Warning, +) +from typing import Any, Final, Literal, TypeVar, overload +from typing_extensions import TypeAlias + +if sys.version_info >= (3, 11): + from sqlite3 import Blob as Blob + +_T = TypeVar("_T") +_ConnectionT = TypeVar("_ConnectionT", bound=Connection) +_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None +_Adapter: TypeAlias = Callable[[_T], _SqliteData] +_Converter: TypeAlias = Callable[[bytes], Any] + +PARSE_COLNAMES: Final[int] +PARSE_DECLTYPES: Final[int] +SQLITE_ALTER_TABLE: Final[int] +SQLITE_ANALYZE: Final[int] +SQLITE_ATTACH: Final[int] +SQLITE_CREATE_INDEX: Final[int] +SQLITE_CREATE_TABLE: Final[int] +SQLITE_CREATE_TEMP_INDEX: Final[int] +SQLITE_CREATE_TEMP_TABLE: Final[int] +SQLITE_CREATE_TEMP_TRIGGER: Final[int] +SQLITE_CREATE_TEMP_VIEW: Final[int] +SQLITE_CREATE_TRIGGER: Final[int] +SQLITE_CREATE_VIEW: Final[int] +SQLITE_CREATE_VTABLE: Final[int] +SQLITE_DELETE: Final[int] +SQLITE_DENY: Final[int] +SQLITE_DETACH: Final[int] +SQLITE_DONE: Final[int] +SQLITE_DROP_INDEX: Final[int] +SQLITE_DROP_TABLE: Final[int] +SQLITE_DROP_TEMP_INDEX: Final[int] +SQLITE_DROP_TEMP_TABLE: Final[int] +SQLITE_DROP_TEMP_TRIGGER: Final[int] +SQLITE_DROP_TEMP_VIEW: Final[int] +SQLITE_DROP_TRIGGER: Final[int] +SQLITE_DROP_VIEW: Final[int] +SQLITE_DROP_VTABLE: Final[int] +SQLITE_FUNCTION: Final[int] +SQLITE_IGNORE: Final[int] +SQLITE_INSERT: Final[int] +SQLITE_OK: Final[int] +SQLITE_PRAGMA: Final[int] +SQLITE_READ: Final[int] +SQLITE_RECURSIVE: Final[int] +SQLITE_REINDEX: Final[int] +SQLITE_SAVEPOINT: Final[int] +SQLITE_SELECT: Final[int] +SQLITE_TRANSACTION: Final[int] +SQLITE_UPDATE: Final[int] +adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]] +converters: dict[str, _Converter] +sqlite_version: str + +if sys.version_info < (3, 12): + version: str + +if sys.version_info >= (3, 12): + LEGACY_TRANSACTION_CONTROL: Final[int] + SQLITE_DBCONFIG_DEFENSIVE: Final[int] + SQLITE_DBCONFIG_DQS_DDL: Final[int] + SQLITE_DBCONFIG_DQS_DML: Final[int] + SQLITE_DBCONFIG_ENABLE_FKEY: Final[int] + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: Final[int] + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: Final[int] + SQLITE_DBCONFIG_ENABLE_QPSG: Final[int] + SQLITE_DBCONFIG_ENABLE_TRIGGER: Final[int] + SQLITE_DBCONFIG_ENABLE_VIEW: Final[int] + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: Final[int] + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: Final[int] + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: Final[int] + SQLITE_DBCONFIG_RESET_DATABASE: Final[int] + SQLITE_DBCONFIG_TRIGGER_EQP: Final[int] + SQLITE_DBCONFIG_TRUSTED_SCHEMA: Final[int] + SQLITE_DBCONFIG_WRITABLE_SCHEMA: Final[int] + +if sys.version_info >= (3, 11): + SQLITE_ABORT: Final[int] + SQLITE_ABORT_ROLLBACK: Final[int] + SQLITE_AUTH: Final[int] + SQLITE_AUTH_USER: Final[int] + SQLITE_BUSY: Final[int] + SQLITE_BUSY_RECOVERY: Final[int] + SQLITE_BUSY_SNAPSHOT: Final[int] + SQLITE_BUSY_TIMEOUT: Final[int] + SQLITE_CANTOPEN: Final[int] + SQLITE_CANTOPEN_CONVPATH: Final[int] + SQLITE_CANTOPEN_DIRTYWAL: Final[int] + SQLITE_CANTOPEN_FULLPATH: Final[int] + SQLITE_CANTOPEN_ISDIR: Final[int] + SQLITE_CANTOPEN_NOTEMPDIR: Final[int] + SQLITE_CANTOPEN_SYMLINK: Final[int] + SQLITE_CONSTRAINT: Final[int] + SQLITE_CONSTRAINT_CHECK: Final[int] + SQLITE_CONSTRAINT_COMMITHOOK: Final[int] + SQLITE_CONSTRAINT_FOREIGNKEY: Final[int] + SQLITE_CONSTRAINT_FUNCTION: Final[int] + SQLITE_CONSTRAINT_NOTNULL: Final[int] + SQLITE_CONSTRAINT_PINNED: Final[int] + SQLITE_CONSTRAINT_PRIMARYKEY: Final[int] + SQLITE_CONSTRAINT_ROWID: Final[int] + SQLITE_CONSTRAINT_TRIGGER: Final[int] + SQLITE_CONSTRAINT_UNIQUE: Final[int] + SQLITE_CONSTRAINT_VTAB: Final[int] + SQLITE_CORRUPT: Final[int] + SQLITE_CORRUPT_INDEX: Final[int] + SQLITE_CORRUPT_SEQUENCE: Final[int] + SQLITE_CORRUPT_VTAB: Final[int] + SQLITE_EMPTY: Final[int] + SQLITE_ERROR: Final[int] + SQLITE_ERROR_MISSING_COLLSEQ: Final[int] + SQLITE_ERROR_RETRY: Final[int] + SQLITE_ERROR_SNAPSHOT: Final[int] + SQLITE_FORMAT: Final[int] + SQLITE_FULL: Final[int] + SQLITE_INTERNAL: Final[int] + SQLITE_INTERRUPT: Final[int] + SQLITE_IOERR: Final[int] + SQLITE_IOERR_ACCESS: Final[int] + SQLITE_IOERR_AUTH: Final[int] + SQLITE_IOERR_BEGIN_ATOMIC: Final[int] + SQLITE_IOERR_BLOCKED: Final[int] + SQLITE_IOERR_CHECKRESERVEDLOCK: Final[int] + SQLITE_IOERR_CLOSE: Final[int] + SQLITE_IOERR_COMMIT_ATOMIC: Final[int] + SQLITE_IOERR_CONVPATH: Final[int] + SQLITE_IOERR_CORRUPTFS: Final[int] + SQLITE_IOERR_DATA: Final[int] + SQLITE_IOERR_DELETE: Final[int] + SQLITE_IOERR_DELETE_NOENT: Final[int] + SQLITE_IOERR_DIR_CLOSE: Final[int] + SQLITE_IOERR_DIR_FSYNC: Final[int] + SQLITE_IOERR_FSTAT: Final[int] + SQLITE_IOERR_FSYNC: Final[int] + SQLITE_IOERR_GETTEMPPATH: Final[int] + SQLITE_IOERR_LOCK: Final[int] + SQLITE_IOERR_MMAP: Final[int] + SQLITE_IOERR_NOMEM: Final[int] + SQLITE_IOERR_RDLOCK: Final[int] + SQLITE_IOERR_READ: Final[int] + SQLITE_IOERR_ROLLBACK_ATOMIC: Final[int] + SQLITE_IOERR_SEEK: Final[int] + SQLITE_IOERR_SHMLOCK: Final[int] + SQLITE_IOERR_SHMMAP: Final[int] + SQLITE_IOERR_SHMOPEN: Final[int] + SQLITE_IOERR_SHMSIZE: Final[int] + SQLITE_IOERR_SHORT_READ: Final[int] + SQLITE_IOERR_TRUNCATE: Final[int] + SQLITE_IOERR_UNLOCK: Final[int] + SQLITE_IOERR_VNODE: Final[int] + SQLITE_IOERR_WRITE: Final[int] + SQLITE_LIMIT_ATTACHED: Final[int] + SQLITE_LIMIT_COLUMN: Final[int] + SQLITE_LIMIT_COMPOUND_SELECT: Final[int] + SQLITE_LIMIT_EXPR_DEPTH: Final[int] + SQLITE_LIMIT_FUNCTION_ARG: Final[int] + SQLITE_LIMIT_LENGTH: Final[int] + SQLITE_LIMIT_LIKE_PATTERN_LENGTH: Final[int] + SQLITE_LIMIT_SQL_LENGTH: Final[int] + SQLITE_LIMIT_TRIGGER_DEPTH: Final[int] + SQLITE_LIMIT_VARIABLE_NUMBER: Final[int] + SQLITE_LIMIT_VDBE_OP: Final[int] + SQLITE_LIMIT_WORKER_THREADS: Final[int] + SQLITE_LOCKED: Final[int] + SQLITE_LOCKED_SHAREDCACHE: Final[int] + SQLITE_LOCKED_VTAB: Final[int] + SQLITE_MISMATCH: Final[int] + SQLITE_MISUSE: Final[int] + SQLITE_NOLFS: Final[int] + SQLITE_NOMEM: Final[int] + SQLITE_NOTADB: Final[int] + SQLITE_NOTFOUND: Final[int] + SQLITE_NOTICE: Final[int] + SQLITE_NOTICE_RECOVER_ROLLBACK: Final[int] + SQLITE_NOTICE_RECOVER_WAL: Final[int] + SQLITE_OK_LOAD_PERMANENTLY: Final[int] + SQLITE_OK_SYMLINK: Final[int] + SQLITE_PERM: Final[int] + SQLITE_PROTOCOL: Final[int] + SQLITE_RANGE: Final[int] + SQLITE_READONLY: Final[int] + SQLITE_READONLY_CANTINIT: Final[int] + SQLITE_READONLY_CANTLOCK: Final[int] + SQLITE_READONLY_DBMOVED: Final[int] + SQLITE_READONLY_DIRECTORY: Final[int] + SQLITE_READONLY_RECOVERY: Final[int] + SQLITE_READONLY_ROLLBACK: Final[int] + SQLITE_ROW: Final[int] + SQLITE_SCHEMA: Final[int] + SQLITE_TOOBIG: Final[int] + SQLITE_WARNING: Final[int] + SQLITE_WARNING_AUTOINDEX: Final[int] + threadsafety: Final[int] + +# Can take or return anything depending on what's in the registry. +@overload +def adapt(obj: Any, proto: Any, /) -> Any: ... +@overload +def adapt(obj: Any, proto: Any, alt: _T, /) -> Any | _T: ... +def complete_statement(statement: str) -> bool: ... + +if sys.version_info >= (3, 12): + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + cached_statements: int = 128, + uri: bool = False, + *, + autocommit: bool = ..., + ) -> Connection: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float, + detect_types: int, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, + check_same_thread: bool, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + *, + autocommit: bool = ..., + ) -> _ConnectionT: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + *, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + autocommit: bool = ..., + ) -> _ConnectionT: ... + +else: + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + cached_statements: int = 128, + uri: bool = False, + ) -> Connection: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float, + detect_types: int, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, + check_same_thread: bool, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + ) -> _ConnectionT: ... + @overload + def connect( + database: StrOrBytesPath, + timeout: float = 5.0, + detect_types: int = 0, + isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", + check_same_thread: bool = True, + *, + factory: type[_ConnectionT], + cached_statements: int = 128, + uri: bool = False, + ) -> _ConnectionT: ... + +def enable_callback_tracebacks(enable: bool, /) -> None: ... + +if sys.version_info < (3, 12): + # takes a pos-or-keyword argument because there is a C wrapper + def enable_shared_cache(do_enable: int) -> None: ... + +if sys.version_info >= (3, 10): + def register_adapter(type: type[_T], adapter: _Adapter[_T], /) -> None: ... + def register_converter(typename: str, converter: _Converter, /) -> None: ... + +else: + def register_adapter(type: type[_T], caster: _Adapter[_T], /) -> None: ... + def register_converter(name: str, converter: _Converter, /) -> None: ... + +if sys.version_info < (3, 10): + OptimizedUnicode = str diff --git a/mypy/typeshed/stdlib/_ssl.pyi b/mypy/typeshed/stdlib/_ssl.pyi new file mode 100644 index 0000000000000..938135eb11924 --- /dev/null +++ b/mypy/typeshed/stdlib/_ssl.pyi @@ -0,0 +1,292 @@ +import sys +from _typeshed import ReadableBuffer, StrOrBytesPath +from collections.abc import Callable +from ssl import ( + SSLCertVerificationError as SSLCertVerificationError, + SSLContext, + SSLEOFError as SSLEOFError, + SSLError as SSLError, + SSLObject, + SSLSyscallError as SSLSyscallError, + SSLWantReadError as SSLWantReadError, + SSLWantWriteError as SSLWantWriteError, + SSLZeroReturnError as SSLZeroReturnError, +) +from typing import Any, Literal, TypedDict, final, overload +from typing_extensions import NotRequired, Self, TypeAlias + +_PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray +_PCTRTT: TypeAlias = tuple[tuple[str, str], ...] +_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] +_PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT] + +class _Cipher(TypedDict): + aead: bool + alg_bits: int + auth: str + description: str + digest: str | None + id: int + kea: str + name: str + protocol: str + strength_bits: int + symmetric: str + +class _CertInfo(TypedDict): + subject: tuple[tuple[tuple[str, str], ...], ...] + issuer: tuple[tuple[tuple[str, str], ...], ...] + version: int + serialNumber: str + notBefore: str + notAfter: str + subjectAltName: NotRequired[tuple[tuple[str, str], ...] | None] + OCSP: NotRequired[tuple[str, ...] | None] + caIssuers: NotRequired[tuple[str, ...] | None] + crlDistributionPoints: NotRequired[tuple[str, ...] | None] + +def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None: ... +def RAND_bytes(n: int, /) -> bytes: ... + +if sys.version_info < (3, 12): + def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]: ... + +if sys.version_info < (3, 10): + def RAND_egd(path: str) -> None: ... + +def RAND_status() -> bool: ... +def get_default_verify_paths() -> tuple[str, str, str, str]: ... + +if sys.platform == "win32": + _EnumRetType: TypeAlias = list[tuple[bytes, str, set[str] | bool]] + def enum_certificates(store_name: str) -> _EnumRetType: ... + def enum_crls(store_name: str) -> _EnumRetType: ... + +def txt2obj(txt: str, name: bool = False) -> tuple[int, str, str, str]: ... +def nid2obj(nid: int, /) -> tuple[int, str, str, str]: ... + +class _SSLContext: + check_hostname: bool + keylog_filename: str | None + maximum_version: int + minimum_version: int + num_tickets: int + options: int + post_handshake_auth: bool + protocol: int + if sys.version_info >= (3, 10): + security_level: int + sni_callback: Callable[[SSLObject, str, SSLContext], None | int] | None + verify_flags: int + verify_mode: int + def __new__(cls, protocol: int, /) -> Self: ... + def cert_store_stats(self) -> dict[str, int]: ... + @overload + def get_ca_certs(self, binary_form: Literal[False] = False) -> list[_PeerCertRetDictType]: ... + @overload + def get_ca_certs(self, binary_form: Literal[True]) -> list[bytes]: ... + @overload + def get_ca_certs(self, binary_form: bool = False) -> Any: ... + def get_ciphers(self) -> list[_Cipher]: ... + def load_cert_chain( + self, certfile: StrOrBytesPath, keyfile: StrOrBytesPath | None = None, password: _PasswordType | None = None + ) -> None: ... + def load_dh_params(self, path: str, /) -> None: ... + def load_verify_locations( + self, + cafile: StrOrBytesPath | None = None, + capath: StrOrBytesPath | None = None, + cadata: str | ReadableBuffer | None = None, + ) -> None: ... + def session_stats(self) -> dict[str, int]: ... + def set_ciphers(self, cipherlist: str, /) -> None: ... + def set_default_verify_paths(self) -> None: ... + def set_ecdh_curve(self, name: str, /) -> None: ... + if sys.version_info >= (3, 13): + def set_psk_client_callback(self, callback: Callable[[str | None], tuple[str | None, bytes]] | None) -> None: ... + def set_psk_server_callback( + self, callback: Callable[[str | None], bytes] | None, identity_hint: str | None = None + ) -> None: ... + +@final +class MemoryBIO: + eof: bool + pending: int + def __new__(self) -> Self: ... + def read(self, size: int = -1, /) -> bytes: ... + def write(self, b: ReadableBuffer, /) -> int: ... + def write_eof(self) -> None: ... + +@final +class SSLSession: + @property + def has_ticket(self) -> bool: ... + @property + def id(self) -> bytes: ... + @property + def ticket_lifetime_hint(self) -> int: ... + @property + def time(self) -> int: ... + @property + def timeout(self) -> int: ... + +# _ssl.Certificate is weird: it can't be instantiated or subclassed. +# Instances can only be created via methods of the private _ssl._SSLSocket class, +# for which the relevant method signatures are: +# +# class _SSLSocket: +# def get_unverified_chain(self) -> list[Certificate] | None: ... +# def get_verified_chain(self) -> list[Certificate] | None: ... +# +# You can find a _ssl._SSLSocket object as the _sslobj attribute of a ssl.SSLSocket object + +if sys.version_info >= (3, 10): + @final + class Certificate: + def get_info(self) -> _CertInfo: ... + @overload + def public_bytes(self) -> str: ... + @overload + def public_bytes(self, format: Literal[1] = 1, /) -> str: ... # ENCODING_PEM + @overload + def public_bytes(self, format: Literal[2], /) -> bytes: ... # ENCODING_DER + @overload + def public_bytes(self, format: int, /) -> str | bytes: ... + +if sys.version_info < (3, 12): + err_codes_to_names: dict[tuple[int, int], str] + err_names_to_codes: dict[str, tuple[int, int]] + lib_codes_to_names: dict[int, str] + +_DEFAULT_CIPHERS: str + +# SSL error numbers +SSL_ERROR_ZERO_RETURN: int +SSL_ERROR_WANT_READ: int +SSL_ERROR_WANT_WRITE: int +SSL_ERROR_WANT_X509_LOOKUP: int +SSL_ERROR_SYSCALL: int +SSL_ERROR_SSL: int +SSL_ERROR_WANT_CONNECT: int +SSL_ERROR_EOF: int +SSL_ERROR_INVALID_ERROR_CODE: int + +# verify modes +CERT_NONE: int +CERT_OPTIONAL: int +CERT_REQUIRED: int + +# verify flags +VERIFY_DEFAULT: int +VERIFY_CRL_CHECK_LEAF: int +VERIFY_CRL_CHECK_CHAIN: int +VERIFY_X509_STRICT: int +VERIFY_X509_TRUSTED_FIRST: int +if sys.version_info >= (3, 10): + VERIFY_ALLOW_PROXY_CERTS: int + VERIFY_X509_PARTIAL_CHAIN: int + +# alert descriptions +ALERT_DESCRIPTION_CLOSE_NOTIFY: int +ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: int +ALERT_DESCRIPTION_BAD_RECORD_MAC: int +ALERT_DESCRIPTION_RECORD_OVERFLOW: int +ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: int +ALERT_DESCRIPTION_HANDSHAKE_FAILURE: int +ALERT_DESCRIPTION_BAD_CERTIFICATE: int +ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: int +ALERT_DESCRIPTION_CERTIFICATE_REVOKED: int +ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: int +ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: int +ALERT_DESCRIPTION_ILLEGAL_PARAMETER: int +ALERT_DESCRIPTION_UNKNOWN_CA: int +ALERT_DESCRIPTION_ACCESS_DENIED: int +ALERT_DESCRIPTION_DECODE_ERROR: int +ALERT_DESCRIPTION_DECRYPT_ERROR: int +ALERT_DESCRIPTION_PROTOCOL_VERSION: int +ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: int +ALERT_DESCRIPTION_INTERNAL_ERROR: int +ALERT_DESCRIPTION_USER_CANCELLED: int +ALERT_DESCRIPTION_NO_RENEGOTIATION: int +ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: int +ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: int +ALERT_DESCRIPTION_UNRECOGNIZED_NAME: int +ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: int +ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: int +ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: int + +# protocol versions +PROTOCOL_SSLv23: int +PROTOCOL_TLS: int +PROTOCOL_TLS_CLIENT: int +PROTOCOL_TLS_SERVER: int +PROTOCOL_TLSv1: int +PROTOCOL_TLSv1_1: int +PROTOCOL_TLSv1_2: int + +# protocol options +OP_ALL: int +OP_NO_SSLv2: int +OP_NO_SSLv3: int +OP_NO_TLSv1: int +OP_NO_TLSv1_1: int +OP_NO_TLSv1_2: int +OP_NO_TLSv1_3: int +OP_CIPHER_SERVER_PREFERENCE: int +OP_SINGLE_DH_USE: int +OP_NO_TICKET: int +OP_SINGLE_ECDH_USE: int +OP_NO_COMPRESSION: int +OP_ENABLE_MIDDLEBOX_COMPAT: int +OP_NO_RENEGOTIATION: int +if sys.version_info >= (3, 11): + OP_IGNORE_UNEXPECTED_EOF: int +elif sys.version_info >= (3, 8) and sys.platform == "linux": + OP_IGNORE_UNEXPECTED_EOF: int +if sys.version_info >= (3, 12): + OP_LEGACY_SERVER_CONNECT: int + OP_ENABLE_KTLS: int + +# host flags +HOSTFLAG_ALWAYS_CHECK_SUBJECT: int +HOSTFLAG_NEVER_CHECK_SUBJECT: int +HOSTFLAG_NO_WILDCARDS: int +HOSTFLAG_NO_PARTIAL_WILDCARDS: int +HOSTFLAG_MULTI_LABEL_WILDCARDS: int +HOSTFLAG_SINGLE_LABEL_SUBDOMAINS: int + +if sys.version_info >= (3, 10): + # certificate file types + # Typed as Literal so the overload on Certificate.public_bytes can work properly. + ENCODING_PEM: Literal[1] + ENCODING_DER: Literal[2] + +# protocol versions +PROTO_MINIMUM_SUPPORTED: int +PROTO_MAXIMUM_SUPPORTED: int +PROTO_SSLv3: int +PROTO_TLSv1: int +PROTO_TLSv1_1: int +PROTO_TLSv1_2: int +PROTO_TLSv1_3: int + +# feature support +HAS_SNI: bool +HAS_TLS_UNIQUE: bool +HAS_ECDH: bool +HAS_NPN: bool +if sys.version_info >= (3, 13): + HAS_PSK: bool +HAS_ALPN: bool +HAS_SSLv2: bool +HAS_SSLv3: bool +HAS_TLSv1: bool +HAS_TLSv1_1: bool +HAS_TLSv1_2: bool +HAS_TLSv1_3: bool + +# version info +OPENSSL_VERSION_NUMBER: int +OPENSSL_VERSION_INFO: tuple[int, int, int, int, int] +OPENSSL_VERSION: str +_OPENSSL_API_VERSION: tuple[int, int, int, int, int] diff --git a/mypy/typeshed/stdlib/_stat.pyi b/mypy/typeshed/stdlib/_stat.pyi index c4e918d8b57f6..7129a282b5747 100644 --- a/mypy/typeshed/stdlib/_stat.pyi +++ b/mypy/typeshed/stdlib/_stat.pyi @@ -1,68 +1,68 @@ import sys -from typing import Literal - -SF_APPEND: Literal[0x00040000] -SF_ARCHIVED: Literal[0x00010000] -SF_IMMUTABLE: Literal[0x00020000] -SF_NOUNLINK: Literal[0x00100000] -SF_SNAPSHOT: Literal[0x00200000] - -ST_MODE: Literal[0] -ST_INO: Literal[1] -ST_DEV: Literal[2] -ST_NLINK: Literal[3] -ST_UID: Literal[4] -ST_GID: Literal[5] -ST_SIZE: Literal[6] -ST_ATIME: Literal[7] -ST_MTIME: Literal[8] -ST_CTIME: Literal[9] - -S_IFIFO: Literal[0o010000] -S_IFLNK: Literal[0o120000] -S_IFREG: Literal[0o100000] -S_IFSOCK: Literal[0o140000] -S_IFBLK: Literal[0o060000] -S_IFCHR: Literal[0o020000] -S_IFDIR: Literal[0o040000] +from typing import Final + +SF_APPEND: Final = 0x00040000 +SF_ARCHIVED: Final = 0x00010000 +SF_IMMUTABLE: Final = 0x00020000 +SF_NOUNLINK: Final = 0x00100000 +SF_SNAPSHOT: Final = 0x00200000 + +ST_MODE: Final = 0 +ST_INO: Final = 1 +ST_DEV: Final = 2 +ST_NLINK: Final = 3 +ST_UID: Final = 4 +ST_GID: Final = 5 +ST_SIZE: Final = 6 +ST_ATIME: Final = 7 +ST_MTIME: Final = 8 +ST_CTIME: Final = 9 + +S_IFIFO: Final = 0o010000 +S_IFLNK: Final = 0o120000 +S_IFREG: Final = 0o100000 +S_IFSOCK: Final = 0o140000 +S_IFBLK: Final = 0o060000 +S_IFCHR: Final = 0o020000 +S_IFDIR: Final = 0o040000 # These are 0 on systems that don't support the specific kind of file. # Example: Linux doesn't support door files, so S_IFDOOR is 0 on linux. -S_IFDOOR: int -S_IFPORT: int -S_IFWHT: int - -S_ISUID: Literal[0o4000] -S_ISGID: Literal[0o2000] -S_ISVTX: Literal[0o1000] - -S_IRWXU: Literal[0o0700] -S_IRUSR: Literal[0o0400] -S_IWUSR: Literal[0o0200] -S_IXUSR: Literal[0o0100] - -S_IRWXG: Literal[0o0070] -S_IRGRP: Literal[0o0040] -S_IWGRP: Literal[0o0020] -S_IXGRP: Literal[0o0010] - -S_IRWXO: Literal[0o0007] -S_IROTH: Literal[0o0004] -S_IWOTH: Literal[0o0002] -S_IXOTH: Literal[0o0001] - -S_ENFMT: Literal[0o2000] -S_IREAD: Literal[0o0400] -S_IWRITE: Literal[0o0200] -S_IEXEC: Literal[0o0100] - -UF_APPEND: Literal[0x00000004] -UF_COMPRESSED: Literal[0x00000020] # OS X 10.6+ only -UF_HIDDEN: Literal[0x00008000] # OX X 10.5+ only -UF_IMMUTABLE: Literal[0x00000002] -UF_NODUMP: Literal[0x00000001] -UF_NOUNLINK: Literal[0x00000010] -UF_OPAQUE: Literal[0x00000008] +S_IFDOOR: Final[int] +S_IFPORT: Final[int] +S_IFWHT: Final[int] + +S_ISUID: Final = 0o4000 +S_ISGID: Final = 0o2000 +S_ISVTX: Final = 0o1000 + +S_IRWXU: Final = 0o0700 +S_IRUSR: Final = 0o0400 +S_IWUSR: Final = 0o0200 +S_IXUSR: Final = 0o0100 + +S_IRWXG: Final = 0o0070 +S_IRGRP: Final = 0o0040 +S_IWGRP: Final = 0o0020 +S_IXGRP: Final = 0o0010 + +S_IRWXO: Final = 0o0007 +S_IROTH: Final = 0o0004 +S_IWOTH: Final = 0o0002 +S_IXOTH: Final = 0o0001 + +S_ENFMT: Final = 0o2000 +S_IREAD: Final = 0o0400 +S_IWRITE: Final = 0o0200 +S_IEXEC: Final = 0o0100 + +UF_APPEND: Final = 0x00000004 +UF_COMPRESSED: Final = 0x00000020 # OS X 10.6+ only +UF_HIDDEN: Final = 0x00008000 # OX X 10.5+ only +UF_IMMUTABLE: Final = 0x00000002 +UF_NODUMP: Final = 0x00000001 +UF_NOUNLINK: Final = 0x00000010 +UF_OPAQUE: Final = 0x00000008 def S_IMODE(mode: int, /) -> int: ... def S_IFMT(mode: int, /) -> int: ... @@ -79,39 +79,41 @@ def S_ISWHT(mode: int, /) -> bool: ... def filemode(mode: int, /) -> str: ... if sys.platform == "win32": - IO_REPARSE_TAG_SYMLINK: int - IO_REPARSE_TAG_MOUNT_POINT: int - IO_REPARSE_TAG_APPEXECLINK: int + IO_REPARSE_TAG_SYMLINK: Final = 0xA000000C + IO_REPARSE_TAG_MOUNT_POINT: Final = 0xA0000003 + IO_REPARSE_TAG_APPEXECLINK: Final = 0x8000001B if sys.platform == "win32": - FILE_ATTRIBUTE_ARCHIVE: Literal[32] - FILE_ATTRIBUTE_COMPRESSED: Literal[2048] - FILE_ATTRIBUTE_DEVICE: Literal[64] - FILE_ATTRIBUTE_DIRECTORY: Literal[16] - FILE_ATTRIBUTE_ENCRYPTED: Literal[16384] - FILE_ATTRIBUTE_HIDDEN: Literal[2] - FILE_ATTRIBUTE_INTEGRITY_STREAM: Literal[32768] - FILE_ATTRIBUTE_NORMAL: Literal[128] - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Literal[8192] - FILE_ATTRIBUTE_NO_SCRUB_DATA: Literal[131072] - FILE_ATTRIBUTE_OFFLINE: Literal[4096] - FILE_ATTRIBUTE_READONLY: Literal[1] - FILE_ATTRIBUTE_REPARSE_POINT: Literal[1024] - FILE_ATTRIBUTE_SPARSE_FILE: Literal[512] - FILE_ATTRIBUTE_SYSTEM: Literal[4] - FILE_ATTRIBUTE_TEMPORARY: Literal[256] - FILE_ATTRIBUTE_VIRTUAL: Literal[65536] + FILE_ATTRIBUTE_ARCHIVE: Final = 32 + FILE_ATTRIBUTE_COMPRESSED: Final = 2048 + FILE_ATTRIBUTE_DEVICE: Final = 64 + FILE_ATTRIBUTE_DIRECTORY: Final = 16 + FILE_ATTRIBUTE_ENCRYPTED: Final = 16384 + FILE_ATTRIBUTE_HIDDEN: Final = 2 + FILE_ATTRIBUTE_INTEGRITY_STREAM: Final = 32768 + FILE_ATTRIBUTE_NORMAL: Final = 128 + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: Final = 8192 + FILE_ATTRIBUTE_NO_SCRUB_DATA: Final = 131072 + FILE_ATTRIBUTE_OFFLINE: Final = 4096 + FILE_ATTRIBUTE_READONLY: Final = 1 + FILE_ATTRIBUTE_REPARSE_POINT: Final = 1024 + FILE_ATTRIBUTE_SPARSE_FILE: Final = 512 + FILE_ATTRIBUTE_SYSTEM: Final = 4 + FILE_ATTRIBUTE_TEMPORARY: Final = 256 + FILE_ATTRIBUTE_VIRTUAL: Final = 65536 if sys.version_info >= (3, 13): - SF_SETTABLE: Literal[0x3FFF0000] + # Varies by platform. + SF_SETTABLE: Final[int] # https://github.com/python/cpython/issues/114081#issuecomment-2119017790 # SF_RESTRICTED: Literal[0x00080000] - SF_FIRMLINK: Literal[0x00800000] - SF_DATALESS: Literal[0x40000000] + SF_FIRMLINK: Final = 0x00800000 + SF_DATALESS: Final = 0x40000000 - SF_SUPPORTED: Literal[0x9F0000] - SF_SYNTHETIC: Literal[0xC0000000] + if sys.platform == "darwin": + SF_SUPPORTED: Final = 0x9F0000 + SF_SYNTHETIC: Final = 0xC0000000 - UF_TRACKED: Literal[0x00000040] - UF_DATAVAULT: Literal[0x00000080] - UF_SETTABLE: Literal[0x0000FFFF] + UF_TRACKED: Final = 0x00000040 + UF_DATAVAULT: Final = 0x00000080 + UF_SETTABLE: Final = 0x0000FFFF diff --git a/mypy/typeshed/stdlib/_struct.pyi b/mypy/typeshed/stdlib/_struct.pyi new file mode 100644 index 0000000000000..662170e869f31 --- /dev/null +++ b/mypy/typeshed/stdlib/_struct.pyi @@ -0,0 +1,22 @@ +from _typeshed import ReadableBuffer, WriteableBuffer +from collections.abc import Iterator +from typing import Any + +def pack(fmt: str | bytes, /, *v: Any) -> bytes: ... +def pack_into(fmt: str | bytes, buffer: WriteableBuffer, offset: int, /, *v: Any) -> None: ... +def unpack(format: str | bytes, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... +def unpack_from(format: str | bytes, /, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... +def iter_unpack(format: str | bytes, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... +def calcsize(format: str | bytes, /) -> int: ... + +class Struct: + @property + def format(self) -> str: ... + @property + def size(self) -> int: ... + def __init__(self, format: str | bytes) -> None: ... + def pack(self, *v: Any) -> bytes: ... + def pack_into(self, buffer: WriteableBuffer, offset: int, *v: Any) -> None: ... + def unpack(self, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... + def unpack_from(self, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... + def iter_unpack(self, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index 4ea9aa0609e58..f0b70ed2a0b05 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -1,3 +1,4 @@ +import signal import sys from _typeshed import structseq from collections.abc import Callable @@ -13,23 +14,54 @@ error = RuntimeError def _count() -> int: ... @final class LockType: - def acquire(self, blocking: bool = ..., timeout: float = ...) -> bool: ... + def acquire(self, blocking: bool = True, timeout: float = -1) -> bool: ... def release(self) -> None: ... def locked(self) -> bool: ... + def acquire_lock(self, blocking: bool = True, timeout: float = -1) -> bool: ... + def release_lock(self) -> None: ... + def locked_lock(self) -> bool: ... def __enter__(self) -> bool: ... def __exit__( self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: ... +if sys.version_info >= (3, 13): + @final + class _ThreadHandle: + ident: int + + def join(self, timeout: float | None = None, /) -> None: ... + def is_done(self) -> bool: ... + def _set_done(self) -> None: ... + + def start_joinable_thread( + function: Callable[[], object], handle: _ThreadHandle | None = None, daemon: bool = True + ) -> _ThreadHandle: ... + lock = LockType + @overload -def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]]) -> int: ... +def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ... +@overload +def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ... + +# Obsolete synonym for start_new_thread() @overload -def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any]) -> int: ... -def interrupt_main() -> None: ... +def start_new(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ... +@overload +def start_new(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ... + +if sys.version_info >= (3, 10): + def interrupt_main(signum: signal.Signals = ..., /) -> None: ... + +else: + def interrupt_main() -> None: ... + def exit() -> NoReturn: ... +def exit_thread() -> NoReturn: ... # Obsolete synonym for exit() def allocate_lock() -> LockType: ... +def allocate() -> LockType: ... # Obsolete synonym for allocate_lock() def get_ident() -> int: ... -def stack_size(size: int = ...) -> int: ... +def stack_size(size: int = 0, /) -> int: ... TIMEOUT_MAX: float diff --git a/mypy/typeshed/stdlib/_threading_local.pyi b/mypy/typeshed/stdlib/_threading_local.pyi index 98683dabcef8f..07a825f0d8168 100644 --- a/mypy/typeshed/stdlib/_threading_local.pyi +++ b/mypy/typeshed/stdlib/_threading_local.pyi @@ -1,5 +1,6 @@ +from threading import RLock from typing import Any -from typing_extensions import TypeAlias +from typing_extensions import Self, TypeAlias from weakref import ReferenceType __all__ = ["local"] @@ -8,10 +9,14 @@ _LocalDict: TypeAlias = dict[Any, Any] class _localimpl: key: str dicts: dict[int, tuple[ReferenceType[Any], _LocalDict]] + # Keep localargs in sync with the *args, **kwargs annotation on local.__new__ + localargs: tuple[list[Any], dict[str, Any]] + locallock: RLock def get_dict(self) -> _LocalDict: ... def create_dict(self) -> _LocalDict: ... class local: + def __new__(cls, /, *args: Any, **kw: Any) -> Self: ... def __getattribute__(self, name: str) -> Any: ... def __setattr__(self, name: str, value: Any) -> None: ... def __delattr__(self, name: str) -> None: ... diff --git a/mypy/typeshed/stdlib/_tkinter.pyi b/mypy/typeshed/stdlib/_tkinter.pyi index aea74c8be279e..63b1e7ca7cb46 100644 --- a/mypy/typeshed/stdlib/_tkinter.pyi +++ b/mypy/typeshed/stdlib/_tkinter.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Callable -from typing import Any, ClassVar, Literal, final +from typing import Any, ClassVar, Final, final from typing_extensions import TypeAlias # _tkinter is meant to be only used internally by tkinter, but some tkinter @@ -95,19 +95,19 @@ class TkappType: def settrace(self, func: _TkinterTraceFunc | None, /) -> None: ... # These should be kept in sync with tkinter.tix constants, except ALL_EVENTS which doesn't match TCL_ALL_EVENTS -ALL_EVENTS: Literal[-3] -FILE_EVENTS: Literal[8] -IDLE_EVENTS: Literal[32] -TIMER_EVENTS: Literal[16] -WINDOW_EVENTS: Literal[4] +ALL_EVENTS: Final = -3 +FILE_EVENTS: Final = 8 +IDLE_EVENTS: Final = 32 +TIMER_EVENTS: Final = 16 +WINDOW_EVENTS: Final = 4 -DONT_WAIT: Literal[2] -EXCEPTION: Literal[8] -READABLE: Literal[2] -WRITABLE: Literal[4] +DONT_WAIT: Final = 2 +EXCEPTION: Final = 8 +READABLE: Final = 2 +WRITABLE: Final = 4 -TCL_VERSION: str -TK_VERSION: str +TCL_VERSION: Final[str] +TK_VERSION: Final[str] @final class TkttType: diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index f142820c56c72..a744340afaabd 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -1,37 +1,10 @@ -import sys from collections.abc import Callable -from typing import Any, Generic, TypeVar, final, overload -from typing_extensions import Self - -if sys.version_info >= (3, 9): - from types import GenericAlias +from typing import Any, TypeVar, overload +from weakref import CallableProxyType as CallableProxyType, ProxyType as ProxyType, ReferenceType as ReferenceType, ref as ref _C = TypeVar("_C", bound=Callable[..., Any]) _T = TypeVar("_T") -@final -class CallableProxyType(Generic[_C]): # "weakcallableproxy" - def __eq__(self, value: object, /) -> bool: ... - def __getattr__(self, attr: str) -> Any: ... - __call__: _C - -@final -class ProxyType(Generic[_T]): # "weakproxy" - def __eq__(self, value: object, /) -> bool: ... - def __getattr__(self, attr: str) -> Any: ... - -class ReferenceType(Generic[_T]): - __callback__: Callable[[Self], Any] - def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ... - def __init__(self, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> None: ... - def __call__(self) -> _T | None: ... - def __eq__(self, value: object, /) -> bool: ... - def __hash__(self) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - -ref = ReferenceType - def getweakrefcount(object: Any, /) -> int: ... def getweakrefs(object: Any, /) -> list[Any]: ... diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index c6fb0484df8e2..0f71a06877481 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -1,117 +1,131 @@ import sys from _typeshed import ReadableBuffer from collections.abc import Sequence -from typing import Any, Literal, NoReturn, final, overload +from typing import Any, Final, Literal, NoReturn, final, overload if sys.platform == "win32": - ABOVE_NORMAL_PRIORITY_CLASS: Literal[0x8000] - BELOW_NORMAL_PRIORITY_CLASS: Literal[0x4000] - - CREATE_BREAKAWAY_FROM_JOB: Literal[0x1000000] - CREATE_DEFAULT_ERROR_MODE: Literal[0x4000000] - CREATE_NO_WINDOW: Literal[0x8000000] - CREATE_NEW_CONSOLE: Literal[0x10] - CREATE_NEW_PROCESS_GROUP: Literal[0x200] - - DETACHED_PROCESS: Literal[8] - DUPLICATE_CLOSE_SOURCE: Literal[1] - DUPLICATE_SAME_ACCESS: Literal[2] - - ERROR_ALREADY_EXISTS: Literal[183] - ERROR_BROKEN_PIPE: Literal[109] - ERROR_IO_PENDING: Literal[997] - ERROR_MORE_DATA: Literal[234] - ERROR_NETNAME_DELETED: Literal[64] - ERROR_NO_DATA: Literal[232] - ERROR_NO_SYSTEM_RESOURCES: Literal[1450] - ERROR_OPERATION_ABORTED: Literal[995] - ERROR_PIPE_BUSY: Literal[231] - ERROR_PIPE_CONNECTED: Literal[535] - ERROR_SEM_TIMEOUT: Literal[121] - - FILE_FLAG_FIRST_PIPE_INSTANCE: Literal[0x80000] - FILE_FLAG_OVERLAPPED: Literal[0x40000000] - - FILE_GENERIC_READ: Literal[1179785] - FILE_GENERIC_WRITE: Literal[1179926] - - FILE_MAP_ALL_ACCESS: Literal[983071] - FILE_MAP_COPY: Literal[1] - FILE_MAP_EXECUTE: Literal[32] - FILE_MAP_READ: Literal[4] - FILE_MAP_WRITE: Literal[2] - - FILE_TYPE_CHAR: Literal[2] - FILE_TYPE_DISK: Literal[1] - FILE_TYPE_PIPE: Literal[3] - FILE_TYPE_REMOTE: Literal[32768] - FILE_TYPE_UNKNOWN: Literal[0] - - GENERIC_READ: Literal[0x80000000] - GENERIC_WRITE: Literal[0x40000000] - HIGH_PRIORITY_CLASS: Literal[0x80] - INFINITE: Literal[0xFFFFFFFF] + ABOVE_NORMAL_PRIORITY_CLASS: Final = 0x8000 + BELOW_NORMAL_PRIORITY_CLASS: Final = 0x4000 + + CREATE_BREAKAWAY_FROM_JOB: Final = 0x1000000 + CREATE_DEFAULT_ERROR_MODE: Final = 0x4000000 + CREATE_NO_WINDOW: Final = 0x8000000 + CREATE_NEW_CONSOLE: Final = 0x10 + CREATE_NEW_PROCESS_GROUP: Final = 0x200 + + DETACHED_PROCESS: Final = 8 + DUPLICATE_CLOSE_SOURCE: Final = 1 + DUPLICATE_SAME_ACCESS: Final = 2 + + ERROR_ALREADY_EXISTS: Final = 183 + ERROR_BROKEN_PIPE: Final = 109 + ERROR_IO_PENDING: Final = 997 + ERROR_MORE_DATA: Final = 234 + ERROR_NETNAME_DELETED: Final = 64 + ERROR_NO_DATA: Final = 232 + ERROR_NO_SYSTEM_RESOURCES: Final = 1450 + ERROR_OPERATION_ABORTED: Final = 995 + ERROR_PIPE_BUSY: Final = 231 + ERROR_PIPE_CONNECTED: Final = 535 + ERROR_SEM_TIMEOUT: Final = 121 + + FILE_FLAG_FIRST_PIPE_INSTANCE: Final = 0x80000 + FILE_FLAG_OVERLAPPED: Final = 0x40000000 + + FILE_GENERIC_READ: Final = 1179785 + FILE_GENERIC_WRITE: Final = 1179926 + + FILE_MAP_ALL_ACCESS: Final = 983071 + FILE_MAP_COPY: Final = 1 + FILE_MAP_EXECUTE: Final = 32 + FILE_MAP_READ: Final = 4 + FILE_MAP_WRITE: Final = 2 + + FILE_TYPE_CHAR: Final = 2 + FILE_TYPE_DISK: Final = 1 + FILE_TYPE_PIPE: Final = 3 + FILE_TYPE_REMOTE: Final = 32768 + FILE_TYPE_UNKNOWN: Final = 0 + + GENERIC_READ: Final = 0x80000000 + GENERIC_WRITE: Final = 0x40000000 + HIGH_PRIORITY_CLASS: Final = 0x80 + INFINITE: Final = 0xFFFFFFFF # Ignore the Flake8 error -- flake8-pyi assumes # most numbers this long will be implementation details, # but here we can see that it's a power of 2 - INVALID_HANDLE_VALUE: Literal[0xFFFFFFFFFFFFFFFF] # noqa: Y054 - IDLE_PRIORITY_CLASS: Literal[0x40] - NORMAL_PRIORITY_CLASS: Literal[0x20] - REALTIME_PRIORITY_CLASS: Literal[0x100] - NMPWAIT_WAIT_FOREVER: Literal[0xFFFFFFFF] - - MEM_COMMIT: Literal[0x1000] - MEM_FREE: Literal[0x10000] - MEM_IMAGE: Literal[0x1000000] - MEM_MAPPED: Literal[0x40000] - MEM_PRIVATE: Literal[0x20000] - MEM_RESERVE: Literal[0x2000] - - NULL: Literal[0] - OPEN_EXISTING: Literal[3] - - PIPE_ACCESS_DUPLEX: Literal[3] - PIPE_ACCESS_INBOUND: Literal[1] - PIPE_READMODE_MESSAGE: Literal[2] - PIPE_TYPE_MESSAGE: Literal[4] - PIPE_UNLIMITED_INSTANCES: Literal[255] - PIPE_WAIT: Literal[0] - - PAGE_EXECUTE: Literal[0x10] - PAGE_EXECUTE_READ: Literal[0x20] - PAGE_EXECUTE_READWRITE: Literal[0x40] - PAGE_EXECUTE_WRITECOPY: Literal[0x80] - PAGE_GUARD: Literal[0x100] - PAGE_NOACCESS: Literal[0x1] - PAGE_NOCACHE: Literal[0x200] - PAGE_READONLY: Literal[0x2] - PAGE_READWRITE: Literal[0x4] - PAGE_WRITECOMBINE: Literal[0x400] - PAGE_WRITECOPY: Literal[0x8] - - PROCESS_ALL_ACCESS: Literal[0x1FFFFF] - PROCESS_DUP_HANDLE: Literal[0x40] - - SEC_COMMIT: Literal[0x8000000] - SEC_IMAGE: Literal[0x1000000] - SEC_LARGE_PAGES: Literal[0x80000000] - SEC_NOCACHE: Literal[0x10000000] - SEC_RESERVE: Literal[0x4000000] - SEC_WRITECOMBINE: Literal[0x40000000] - - STARTF_USESHOWWINDOW: Literal[0x1] - STARTF_USESTDHANDLES: Literal[0x100] - - STD_ERROR_HANDLE: Literal[0xFFFFFFF4] - STD_OUTPUT_HANDLE: Literal[0xFFFFFFF5] - STD_INPUT_HANDLE: Literal[0xFFFFFFF6] - - STILL_ACTIVE: Literal[259] - SW_HIDE: Literal[0] - SYNCHRONIZE: Literal[0x100000] - WAIT_ABANDONED_0: Literal[128] - WAIT_OBJECT_0: Literal[0] - WAIT_TIMEOUT: Literal[258] + INVALID_HANDLE_VALUE: Final = 0xFFFFFFFFFFFFFFFF # noqa: Y054 + IDLE_PRIORITY_CLASS: Final = 0x40 + NORMAL_PRIORITY_CLASS: Final = 0x20 + REALTIME_PRIORITY_CLASS: Final = 0x100 + NMPWAIT_WAIT_FOREVER: Final = 0xFFFFFFFF + + MEM_COMMIT: Final = 0x1000 + MEM_FREE: Final = 0x10000 + MEM_IMAGE: Final = 0x1000000 + MEM_MAPPED: Final = 0x40000 + MEM_PRIVATE: Final = 0x20000 + MEM_RESERVE: Final = 0x2000 + + NULL: Final = 0 + OPEN_EXISTING: Final = 3 + + PIPE_ACCESS_DUPLEX: Final = 3 + PIPE_ACCESS_INBOUND: Final = 1 + PIPE_READMODE_MESSAGE: Final = 2 + PIPE_TYPE_MESSAGE: Final = 4 + PIPE_UNLIMITED_INSTANCES: Final = 255 + PIPE_WAIT: Final = 0 + + PAGE_EXECUTE: Final = 0x10 + PAGE_EXECUTE_READ: Final = 0x20 + PAGE_EXECUTE_READWRITE: Final = 0x40 + PAGE_EXECUTE_WRITECOPY: Final = 0x80 + PAGE_GUARD: Final = 0x100 + PAGE_NOACCESS: Final = 0x1 + PAGE_NOCACHE: Final = 0x200 + PAGE_READONLY: Final = 0x2 + PAGE_READWRITE: Final = 0x4 + PAGE_WRITECOMBINE: Final = 0x400 + PAGE_WRITECOPY: Final = 0x8 + + PROCESS_ALL_ACCESS: Final = 0x1FFFFF + PROCESS_DUP_HANDLE: Final = 0x40 + + SEC_COMMIT: Final = 0x8000000 + SEC_IMAGE: Final = 0x1000000 + SEC_LARGE_PAGES: Final = 0x80000000 + SEC_NOCACHE: Final = 0x10000000 + SEC_RESERVE: Final = 0x4000000 + SEC_WRITECOMBINE: Final = 0x40000000 + + if sys.version_info >= (3, 13): + STARTF_FORCEOFFFEEDBACK: Final = 0x80 + STARTF_FORCEONFEEDBACK: Final = 0x40 + STARTF_PREVENTPINNING: Final = 0x2000 + STARTF_RUNFULLSCREEN: Final = 0x20 + STARTF_TITLEISAPPID: Final = 0x1000 + STARTF_TITLEISLINKNAME: Final = 0x800 + STARTF_UNTRUSTEDSOURCE: Final = 0x8000 + STARTF_USECOUNTCHARS: Final = 0x8 + STARTF_USEFILLATTRIBUTE: Final = 0x10 + STARTF_USEHOTKEY: Final = 0x200 + STARTF_USEPOSITION: Final = 0x4 + STARTF_USESIZE: Final = 0x2 + + STARTF_USESHOWWINDOW: Final = 0x1 + STARTF_USESTDHANDLES: Final = 0x100 + + STD_ERROR_HANDLE: Final = 0xFFFFFFF4 + STD_OUTPUT_HANDLE: Final = 0xFFFFFFF5 + STD_INPUT_HANDLE: Final = 0xFFFFFFF6 + + STILL_ACTIVE: Final = 259 + SW_HIDE: Final = 0 + SYNCHRONIZE: Final = 0x100000 + WAIT_ABANDONED_0: Final = 128 + WAIT_OBJECT_0: Final = 0 + WAIT_TIMEOUT: Final = 258 if sys.version_info >= (3, 10): LOCALE_NAME_INVARIANT: str @@ -131,32 +145,32 @@ if sys.platform == "win32": LCMAP_UPPERCASE: int if sys.version_info >= (3, 12): - COPYFILE2_CALLBACK_CHUNK_STARTED: Literal[1] - COPYFILE2_CALLBACK_CHUNK_FINISHED: Literal[2] - COPYFILE2_CALLBACK_STREAM_STARTED: Literal[3] - COPYFILE2_CALLBACK_STREAM_FINISHED: Literal[4] - COPYFILE2_CALLBACK_POLL_CONTINUE: Literal[5] - COPYFILE2_CALLBACK_ERROR: Literal[6] - - COPYFILE2_PROGRESS_CONTINUE: Literal[0] - COPYFILE2_PROGRESS_CANCEL: Literal[1] - COPYFILE2_PROGRESS_STOP: Literal[2] - COPYFILE2_PROGRESS_QUIET: Literal[3] - COPYFILE2_PROGRESS_PAUSE: Literal[4] - - COPY_FILE_FAIL_IF_EXISTS: Literal[0x1] - COPY_FILE_RESTARTABLE: Literal[0x2] - COPY_FILE_OPEN_SOURCE_FOR_WRITE: Literal[0x4] - COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Literal[0x8] - COPY_FILE_COPY_SYMLINK: Literal[0x800] - COPY_FILE_NO_BUFFERING: Literal[0x1000] - COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Literal[0x2000] - COPY_FILE_RESUME_FROM_PAUSE: Literal[0x4000] - COPY_FILE_NO_OFFLOAD: Literal[0x40000] - COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Literal[0x10000000] - - ERROR_ACCESS_DENIED: Literal[5] - ERROR_PRIVILEGE_NOT_HELD: Literal[1314] + COPYFILE2_CALLBACK_CHUNK_STARTED: Final = 1 + COPYFILE2_CALLBACK_CHUNK_FINISHED: Final = 2 + COPYFILE2_CALLBACK_STREAM_STARTED: Final = 3 + COPYFILE2_CALLBACK_STREAM_FINISHED: Final = 4 + COPYFILE2_CALLBACK_POLL_CONTINUE: Final = 5 + COPYFILE2_CALLBACK_ERROR: Final = 6 + + COPYFILE2_PROGRESS_CONTINUE: Final = 0 + COPYFILE2_PROGRESS_CANCEL: Final = 1 + COPYFILE2_PROGRESS_STOP: Final = 2 + COPYFILE2_PROGRESS_QUIET: Final = 3 + COPYFILE2_PROGRESS_PAUSE: Final = 4 + + COPY_FILE_FAIL_IF_EXISTS: Final = 0x1 + COPY_FILE_RESTARTABLE: Final = 0x2 + COPY_FILE_OPEN_SOURCE_FOR_WRITE: Final = 0x4 + COPY_FILE_ALLOW_DECRYPTED_DESTINATION: Final = 0x8 + COPY_FILE_COPY_SYMLINK: Final = 0x800 + COPY_FILE_NO_BUFFERING: Final = 0x1000 + COPY_FILE_REQUEST_SECURITY_PRIVILEGES: Final = 0x2000 + COPY_FILE_RESUME_FROM_PAUSE: Final = 0x4000 + COPY_FILE_NO_OFFLOAD: Final = 0x40000 + COPY_FILE_REQUEST_COMPRESSED_TRAFFIC: Final = 0x10000000 + + ERROR_ACCESS_DENIED: Final = 5 + ERROR_PRIVILEGE_NOT_HELD: Final = 1314 def CloseHandle(handle: int, /) -> None: ... @overload @@ -250,6 +264,20 @@ if sys.platform == "win32": def cancel(self) -> None: ... def getbuffer(self) -> bytes | None: ... + if sys.version_info >= (3, 13): + def BatchedWaitForMultipleObjects( + handle_seq: Sequence[int], wait_all: bool, milliseconds: int = 0xFFFFFFFF + ) -> list[int]: ... + def CreateEventW(security_attributes: int, manual_reset: bool, initial_state: bool, name: str | None) -> int: ... + def CreateMutexW(security_attributes: int, initial_owner: bool, name: str) -> int: ... + def GetLongPathName(path: str) -> str: ... + def GetShortPathName(path: str) -> str: ... + def OpenEventW(desired_access: int, inherit_handle: bool, name: str) -> int: ... + def OpenMutexW(desired_access: int, inherit_handle: bool, name: str) -> int: ... + def ReleaseMutex(mutex: int) -> None: ... + def ResetEvent(event: int) -> None: ... + def SetEvent(event: int) -> None: ... + if sys.version_info >= (3, 12): def CopyFile2(existing_file_name: str, new_file_name: str, flags: int, progress_routine: int | None = None) -> int: ... def NeedCurrentDirectoryForExePath(exe_name: str, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/abc.pyi b/mypy/typeshed/stdlib/abc.pyi index 6bf7821f1c1b6..fdca48ac7aafe 100644 --- a/mypy/typeshed/stdlib/abc.pyi +++ b/mypy/typeshed/stdlib/abc.pyi @@ -28,17 +28,17 @@ class ABCMeta(type): def register(cls: ABCMeta, subclass: type[_T]) -> type[_T]: ... def abstractmethod(funcobj: _FuncT) -> _FuncT: ... -@deprecated("Deprecated, use 'classmethod' with 'abstractmethod' instead") +@deprecated("Use 'classmethod' with 'abstractmethod' instead") class abstractclassmethod(classmethod[_T, _P, _R_co]): __isabstractmethod__: Literal[True] def __init__(self, callable: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ... -@deprecated("Deprecated, use 'staticmethod' with 'abstractmethod' instead") +@deprecated("Use 'staticmethod' with 'abstractmethod' instead") class abstractstaticmethod(staticmethod[_P, _R_co]): __isabstractmethod__: Literal[True] def __init__(self, callable: Callable[_P, _R_co]) -> None: ... -@deprecated("Deprecated, use 'property' with 'abstractmethod' instead") +@deprecated("Use 'property' with 'abstractmethod' instead") class abstractproperty(property): __isabstractmethod__: Literal[True] diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 1956d08c9933e..2526322ac8f6d 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern -from typing import IO, Any, Generic, Literal, NewType, NoReturn, Protocol, TypeVar, overload +from typing import IO, Any, Final, Generic, NewType, NoReturn, Protocol, TypeVar, overload from typing_extensions import Self, TypeAlias, deprecated __all__ = [ @@ -32,6 +32,7 @@ _T = TypeVar("_T") _ActionT = TypeVar("_ActionT", bound=Action) _ArgumentParserT = TypeVar("_ArgumentParserT", bound=ArgumentParser) _N = TypeVar("_N") +_ActionType: TypeAlias = Callable[[str], Any] | FileType | str # more precisely, Literal["store", "store_const", "store_true", # "store_false", "append", "append_const", "count", "help", "version", # "extend"], but using this would make it hard to annotate callers @@ -42,15 +43,15 @@ _ActionStr: TypeAlias = str # callers that don't use a literal argument _NArgsStr: TypeAlias = str -ONE_OR_MORE: Literal["+"] -OPTIONAL: Literal["?"] -PARSER: Literal["A..."] -REMAINDER: Literal["..."] +ONE_OR_MORE: Final = "+" +OPTIONAL: Final = "?" +PARSER: Final = "A..." +REMAINDER: Final = "..." _SUPPRESS_T = NewType("_SUPPRESS_T", str) SUPPRESS: _SUPPRESS_T | str # not using Literal because argparse sometimes compares SUPPRESS with is # the | str is there so that foo = argparse.SUPPRESS; foo = "test" checks out in mypy -ZERO_OR_MORE: Literal["*"] -_UNRECOGNIZED_ARGS_ATTR: str # undocumented +ZERO_OR_MORE: Final = "*" +_UNRECOGNIZED_ARGS_ATTR: Final[str] # undocumented class ArgumentError(Exception): argument_name: str | None @@ -89,7 +90,7 @@ class _ActionsContainer: nargs: int | _NArgsStr | _SUPPRESS_T | None = None, const: Any = ..., default: Any = ..., - type: Callable[[str], _T] | FileType = ..., + type: _ActionType = ..., choices: Iterable[_T] | None = ..., required: bool = ..., help: str | None = ..., @@ -313,7 +314,7 @@ class Action(_AttributeHolder): nargs: int | str | None const: Any default: Any - type: Callable[[str], Any] | FileType | None + type: _ActionType | None choices: Iterable[Any] | None required: bool help: str | None @@ -356,7 +357,17 @@ class Action(_AttributeHolder): if sys.version_info >= (3, 12): class BooleanOptionalAction(Action): - if sys.version_info >= (3, 13): + if sys.version_info >= (3, 14): + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: bool | None = None, + required: bool = False, + help: str | None = None, + deprecated: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 13): @overload def __init__( self, @@ -699,6 +710,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): add_help: bool = ..., allow_abbrev: bool = ..., exit_on_error: bool = ..., + **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... elif sys.version_info >= (3, 9): def add_parser( @@ -721,6 +733,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): add_help: bool = ..., allow_abbrev: bool = ..., exit_on_error: bool = ..., + **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... else: def add_parser( @@ -742,6 +755,7 @@ class _SubParsersAction(Action, Generic[_ArgumentParserT]): conflict_handler: str = ..., add_help: bool = ..., allow_abbrev: bool = ..., + **kwargs: Any, # Accepting any additional kwargs for custom parser classes ) -> _ArgumentParserT: ... def _get_subactions(self) -> list[Action]: ... diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 90ede461fe3ce..351a4af2fb752 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -1,183 +1,1717 @@ import os import sys -from _ast import * +import typing_extensions +from _ast import ( + PyCF_ALLOW_TOP_LEVEL_AWAIT as PyCF_ALLOW_TOP_LEVEL_AWAIT, + PyCF_ONLY_AST as PyCF_ONLY_AST, + PyCF_TYPE_COMMENTS as PyCF_TYPE_COMMENTS, +) from _typeshed import ReadableBuffer, Unused from collections.abc import Iterator -from typing import Any, Literal, TypeVar as _TypeVar, overload -from typing_extensions import deprecated +from typing import Any, ClassVar, Generic, Literal, TypedDict, TypeVar as _TypeVar, overload +from typing_extensions import Self, Unpack, deprecated -class _ABC(type): - if sys.version_info >= (3, 9): - def __init__(cls, *args: Unused) -> None: ... +if sys.version_info >= (3, 13): + from _ast import PyCF_OPTIMIZED_AST as PyCF_OPTIMIZED_AST -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Num(Constant, metaclass=_ABC): - value: int | float | complex +# Alias used for fields that must always be valid identifiers +# A string `x` counts as a valid identifier if both the following are True +# (1) `x.isidentifier()` evaluates to `True` +# (2) `keyword.iskeyword(x)` evaluates to `False` +_Identifier: typing_extensions.TypeAlias = str -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Str(Constant, metaclass=_ABC): - value: str - # Aliases for value, for backwards compatibility - s: str +# Used for node end positions in constructor keyword arguments +_EndPositionT = typing_extensions.TypeVar("_EndPositionT", int, int | None, default=int | None) -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Bytes(Constant, metaclass=_ABC): - value: bytes - # Aliases for value, for backwards compatibility - s: bytes +# Corresponds to the names in the `_attributes` class variable which is non-empty in certain AST nodes +class _Attributes(TypedDict, Generic[_EndPositionT], total=False): + lineno: int + col_offset: int + end_lineno: _EndPositionT + end_col_offset: _EndPositionT -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class NameConstant(Constant, metaclass=_ABC): ... +# The various AST classes are implemented in C, and imported from _ast at runtime, +# but they consider themselves to live in the ast module, +# so we'll define the stubs in this file. +class AST: + if sys.version_info >= (3, 10): + __match_args__ = () + _attributes: ClassVar[tuple[str, ...]] + _fields: ClassVar[tuple[str, ...]] + if sys.version_info >= (3, 13): + _field_types: ClassVar[dict[str, Any]] -@deprecated("Replaced by ast.Constant; removal scheduled for Python 3.14") -class Ellipsis(Constant, metaclass=_ABC): ... + if sys.version_info >= (3, 14): + def __replace__(self) -> Self: ... -if sys.version_info >= (3, 9): - class slice(AST): ... - class ExtSlice(slice): ... - class Index(slice): ... - class Suite(mod): ... - class AugLoad(expr_context): ... - class AugStore(expr_context): ... - class Param(expr_context): ... +class mod(AST): ... -class NodeVisitor: - def visit(self, node: AST) -> Any: ... - def generic_visit(self, node: AST) -> Any: ... - def visit_Module(self, node: Module) -> Any: ... - def visit_Interactive(self, node: Interactive) -> Any: ... - def visit_Expression(self, node: Expression) -> Any: ... - def visit_FunctionDef(self, node: FunctionDef) -> Any: ... - def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any: ... - def visit_ClassDef(self, node: ClassDef) -> Any: ... - def visit_Return(self, node: Return) -> Any: ... - def visit_Delete(self, node: Delete) -> Any: ... - def visit_Assign(self, node: Assign) -> Any: ... - def visit_AugAssign(self, node: AugAssign) -> Any: ... - def visit_AnnAssign(self, node: AnnAssign) -> Any: ... - def visit_For(self, node: For) -> Any: ... - def visit_AsyncFor(self, node: AsyncFor) -> Any: ... - def visit_While(self, node: While) -> Any: ... - def visit_If(self, node: If) -> Any: ... - def visit_With(self, node: With) -> Any: ... - def visit_AsyncWith(self, node: AsyncWith) -> Any: ... - def visit_Raise(self, node: Raise) -> Any: ... - def visit_Try(self, node: Try) -> Any: ... - def visit_Assert(self, node: Assert) -> Any: ... - def visit_Import(self, node: Import) -> Any: ... - def visit_ImportFrom(self, node: ImportFrom) -> Any: ... - def visit_Global(self, node: Global) -> Any: ... - def visit_Nonlocal(self, node: Nonlocal) -> Any: ... - def visit_Expr(self, node: Expr) -> Any: ... - def visit_Pass(self, node: Pass) -> Any: ... - def visit_Break(self, node: Break) -> Any: ... - def visit_Continue(self, node: Continue) -> Any: ... - def visit_Slice(self, node: Slice) -> Any: ... - def visit_BoolOp(self, node: BoolOp) -> Any: ... - def visit_BinOp(self, node: BinOp) -> Any: ... - def visit_UnaryOp(self, node: UnaryOp) -> Any: ... - def visit_Lambda(self, node: Lambda) -> Any: ... - def visit_IfExp(self, node: IfExp) -> Any: ... - def visit_Dict(self, node: Dict) -> Any: ... - def visit_Set(self, node: Set) -> Any: ... - def visit_ListComp(self, node: ListComp) -> Any: ... - def visit_SetComp(self, node: SetComp) -> Any: ... - def visit_DictComp(self, node: DictComp) -> Any: ... - def visit_GeneratorExp(self, node: GeneratorExp) -> Any: ... - def visit_Await(self, node: Await) -> Any: ... - def visit_Yield(self, node: Yield) -> Any: ... - def visit_YieldFrom(self, node: YieldFrom) -> Any: ... - def visit_Compare(self, node: Compare) -> Any: ... - def visit_Call(self, node: Call) -> Any: ... - def visit_FormattedValue(self, node: FormattedValue) -> Any: ... - def visit_JoinedStr(self, node: JoinedStr) -> Any: ... - def visit_Constant(self, node: Constant) -> Any: ... - def visit_NamedExpr(self, node: NamedExpr) -> Any: ... - def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ... - def visit_Attribute(self, node: Attribute) -> Any: ... - def visit_Subscript(self, node: Subscript) -> Any: ... - def visit_Starred(self, node: Starred) -> Any: ... - def visit_Name(self, node: Name) -> Any: ... - def visit_List(self, node: List) -> Any: ... - def visit_Tuple(self, node: Tuple) -> Any: ... - def visit_Del(self, node: Del) -> Any: ... - def visit_Load(self, node: Load) -> Any: ... - def visit_Store(self, node: Store) -> Any: ... - def visit_And(self, node: And) -> Any: ... - def visit_Or(self, node: Or) -> Any: ... - def visit_Add(self, node: Add) -> Any: ... - def visit_BitAnd(self, node: BitAnd) -> Any: ... - def visit_BitOr(self, node: BitOr) -> Any: ... - def visit_BitXor(self, node: BitXor) -> Any: ... - def visit_Div(self, node: Div) -> Any: ... - def visit_FloorDiv(self, node: FloorDiv) -> Any: ... - def visit_LShift(self, node: LShift) -> Any: ... - def visit_Mod(self, node: Mod) -> Any: ... - def visit_Mult(self, node: Mult) -> Any: ... - def visit_MatMult(self, node: MatMult) -> Any: ... - def visit_Pow(self, node: Pow) -> Any: ... - def visit_RShift(self, node: RShift) -> Any: ... - def visit_Sub(self, node: Sub) -> Any: ... - def visit_Invert(self, node: Invert) -> Any: ... - def visit_Not(self, node: Not) -> Any: ... - def visit_UAdd(self, node: UAdd) -> Any: ... - def visit_USub(self, node: USub) -> Any: ... - def visit_Eq(self, node: Eq) -> Any: ... - def visit_Gt(self, node: Gt) -> Any: ... - def visit_GtE(self, node: GtE) -> Any: ... - def visit_In(self, node: In) -> Any: ... - def visit_Is(self, node: Is) -> Any: ... - def visit_IsNot(self, node: IsNot) -> Any: ... - def visit_Lt(self, node: Lt) -> Any: ... - def visit_LtE(self, node: LtE) -> Any: ... - def visit_NotEq(self, node: NotEq) -> Any: ... - def visit_NotIn(self, node: NotIn) -> Any: ... - def visit_comprehension(self, node: comprehension) -> Any: ... - def visit_ExceptHandler(self, node: ExceptHandler) -> Any: ... - def visit_arguments(self, node: arguments) -> Any: ... - def visit_arg(self, node: arg) -> Any: ... - def visit_keyword(self, node: keyword) -> Any: ... - def visit_alias(self, node: alias) -> Any: ... - def visit_withitem(self, node: withitem) -> Any: ... +class Module(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("body", "type_ignores") + body: list[stmt] + type_ignores: list[TypeIgnore] + if sys.version_info >= (3, 13): + def __init__(self, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> None: ... + else: + def __init__(self, body: list[stmt], type_ignores: list[TypeIgnore]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, body: list[stmt] = ..., type_ignores: list[TypeIgnore] = ...) -> Self: ... + +class Interactive(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("body",) + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__(self, body: list[stmt] = ...) -> None: ... + else: + def __init__(self, body: list[stmt]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, body: list[stmt] = ...) -> Self: ... + +class Expression(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("body",) + body: expr + def __init__(self, body: expr) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, body: expr = ...) -> Self: ... + +class FunctionType(mod): + if sys.version_info >= (3, 10): + __match_args__ = ("argtypes", "returns") + argtypes: list[expr] + returns: expr + if sys.version_info >= (3, 13): + @overload + def __init__(self, argtypes: list[expr], returns: expr) -> None: ... + @overload + def __init__(self, argtypes: list[expr] = ..., *, returns: expr) -> None: ... + else: + def __init__(self, argtypes: list[expr], returns: expr) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, argtypes: list[expr] = ..., returns: expr = ...) -> Self: ... + +class stmt(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... + +class FunctionDef(stmt): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") + elif sys.version_info >= (3, 10): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") + name: _Identifier + args: arguments + body: list[stmt] + decorator_list: list[expr] + returns: expr | None + type_comment: str | None + if sys.version_info >= (3, 12): + type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = None, + type_comment: str | None = None, + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + *, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + args: arguments = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = ..., + type_comment: str | None = ..., + type_params: list[type_param] = ..., + ) -> Self: ... + +class AsyncFunctionDef(stmt): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment", "type_params") + elif sys.version_info >= (3, 10): + __match_args__ = ("name", "args", "body", "decorator_list", "returns", "type_comment") + name: _Identifier + args: arguments + body: list[stmt] + decorator_list: list[expr] + returns: expr | None + type_comment: str | None + if sys.version_info >= (3, 12): + type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + returns: expr | None = None, + type_comment: str | None = None, + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + *, + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + args: arguments, + body: list[stmt], + decorator_list: list[expr], + returns: expr | None = None, + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + args: arguments = ..., + body: list[stmt], + decorator_list: list[expr], + returns: expr | None, + type_comment: str | None, + type_params: list[type_param], + ) -> Self: ... + +class ClassDef(stmt): + if sys.version_info >= (3, 12): + __match_args__ = ("name", "bases", "keywords", "body", "decorator_list", "type_params") + elif sys.version_info >= (3, 10): + __match_args__ = ("name", "bases", "keywords", "body", "decorator_list") + name: _Identifier + bases: list[expr] + keywords: list[keyword] + body: list[stmt] + decorator_list: list[expr] + if sys.version_info >= (3, 12): + type_params: list[type_param] + if sys.version_info >= (3, 13): + def __init__( + self, + name: _Identifier, + bases: list[expr] = ..., + keywords: list[keyword] = ..., + body: list[stmt] = ..., + decorator_list: list[expr] = ..., + type_params: list[type_param] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + elif sys.version_info >= (3, 12): + def __init__( + self, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier, + bases: list[expr], + keywords: list[keyword], + body: list[stmt], + decorator_list: list[expr], + type_params: list[type_param], + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class Return(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr | None + def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Delete(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("targets",) + targets: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, targets: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, targets: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Assign(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("targets", "value", "type_comment") + targets: list[expr] + value: expr + type_comment: str | None + if sys.version_info >= (3, 13): + @overload + def __init__( + self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + @overload + def __init__( + self, targets: list[expr] = ..., *, value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__( + self, targets: list[expr], value: expr, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, targets: list[expr] = ..., value: expr = ..., type_comment: str | None = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +if sys.version_info >= (3, 12): + class TypeAlias(stmt): + __match_args__ = ("name", "type_params", "value") + name: Name + type_params: list[type_param] + value: expr + if sys.version_info >= (3, 13): + @overload + def __init__( + self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + @overload + def __init__( + self, name: Name, type_params: list[type_param] = ..., *, value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__( + self, name: Name, type_params: list[type_param], value: expr, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: Name = ..., + type_params: list[type_param] = ..., + value: expr = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + +class AugAssign(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "op", "value") + target: Name | Attribute | Subscript + op: operator + value: expr + def __init__( + self, target: Name | Attribute | Subscript, op: operator, value: expr, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: Name | Attribute | Subscript = ..., + op: operator = ..., + value: expr = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class AnnAssign(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "annotation", "value", "simple") + target: Name | Attribute | Subscript + annotation: expr + value: expr | None + simple: int + @overload + def __init__( + self, + target: Name | Attribute | Subscript, + annotation: expr, + value: expr | None, + simple: int, + **kwargs: Unpack[_Attributes], + ) -> None: ... + @overload + def __init__( + self, + target: Name | Attribute | Subscript, + annotation: expr, + value: expr | None = None, + *, + simple: int, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: Name | Attribute | Subscript = ..., + annotation: expr = ..., + value: expr | None = ..., + simple: int = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class For(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "iter", "body", "orelse", "type_comment") + target: expr + iter: expr + body: list[stmt] + orelse: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt], + orelse: list[stmt], + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: expr = ..., + iter: expr = ..., + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class AsyncFor(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "iter", "body", "orelse", "type_comment") + target: expr + iter: expr + body: list[stmt] + orelse: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + target: expr, + iter: expr, + body: list[stmt], + orelse: list[stmt], + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + target: expr = ..., + iter: expr = ..., + body: list[stmt] = ..., + orelse: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class While(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "body", "orelse") + test: expr + body: list[stmt] + orelse: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> Self: ... + +class If(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "body", "orelse") + test: expr + body: list[stmt] + orelse: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, test: expr, body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, test: expr, body: list[stmt], orelse: list[stmt], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, test: expr = ..., body: list[stmt] = ..., orelse: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class With(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("items", "body", "type_comment") + items: list[withitem] + body: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class AsyncWith(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("items", "body", "type_comment") + items: list[withitem] + body: list[stmt] + type_comment: str | None + if sys.version_info >= (3, 13): + def __init__( + self, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = None, + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, items: list[withitem], body: list[stmt], type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + items: list[withitem] = ..., + body: list[stmt] = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +if sys.version_info >= (3, 10): + class Match(stmt): + __match_args__ = ("subject", "cases") + subject: expr + cases: list[match_case] + if sys.version_info >= (3, 13): + def __init__(self, subject: expr, cases: list[match_case] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, subject: expr, cases: list[match_case], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, subject: expr = ..., cases: list[match_case] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Raise(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("exc", "cause") + exc: expr | None + cause: expr | None + def __init__(self, exc: expr | None = None, cause: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, exc: expr | None = ..., cause: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Try(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("body", "handlers", "orelse", "finalbody") + body: list[stmt] + handlers: list[ExceptHandler] + orelse: list[stmt] + finalbody: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + body: list[stmt], + handlers: list[ExceptHandler], + orelse: list[stmt], + finalbody: list[stmt], + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +if sys.version_info >= (3, 11): + class TryStar(stmt): + __match_args__ = ("body", "handlers", "orelse", "finalbody") + body: list[stmt] + handlers: list[ExceptHandler] + orelse: list[stmt] + finalbody: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> None: ... + else: + def __init__( + self, + body: list[stmt], + handlers: list[ExceptHandler], + orelse: list[stmt], + finalbody: list[stmt], + **kwargs: Unpack[_Attributes], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + body: list[stmt] = ..., + handlers: list[ExceptHandler] = ..., + orelse: list[stmt] = ..., + finalbody: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class Assert(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "msg") + test: expr + msg: expr | None + def __init__(self, test: expr, msg: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, test: expr, msg: expr | None, **kwargs: Unpack[_Attributes]) -> Self: ... + +class Import(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("names",) + names: list[alias] + if sys.version_info >= (3, 13): + def __init__(self, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[alias], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[alias] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class ImportFrom(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("module", "names", "level") + module: str | None + names: list[alias] + level: int + if sys.version_info >= (3, 13): + @overload + def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__( + self, module: str | None = None, names: list[alias] = ..., *, level: int, **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + @overload + def __init__(self, module: str | None, names: list[alias], level: int, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__( + self, module: str | None = None, *, names: list[alias], level: int, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, module: str | None = ..., names: list[alias] = ..., level: int = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Global(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("names",) + names: list[_Identifier] + if sys.version_info >= (3, 13): + def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> Self: ... + +class Nonlocal(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("names",) + names: list[_Identifier] + if sys.version_info >= (3, 13): + def __init__(self, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, names: list[_Identifier], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, names: list[_Identifier] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Expr(stmt): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Pass(stmt): ... +class Break(stmt): ... +class Continue(stmt): ... + +class expr(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes]) -> Self: ... + +class BoolOp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("op", "values") + op: boolop + values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, op: boolop, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, op: boolop, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, op: boolop = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class NamedExpr(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "value") + target: Name + value: expr + def __init__(self, target: Name, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, target: Name = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class BinOp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("left", "op", "right") + left: expr + op: operator + right: expr + def __init__(self, left: expr, op: operator, right: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, left: expr = ..., op: operator = ..., right: expr = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class UnaryOp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("op", "operand") + op: unaryop + operand: expr + def __init__(self, op: unaryop, operand: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, op: unaryop = ..., operand: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Lambda(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("args", "body") + args: arguments + body: expr + def __init__(self, args: arguments, body: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, args: arguments = ..., body: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class IfExp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("test", "body", "orelse") + test: expr + body: expr + orelse: expr + def __init__(self, test: expr, body: expr, orelse: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, test: expr = ..., body: expr = ..., orelse: expr = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Dict(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("keys", "values") + keys: list[expr | None] + values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, keys: list[expr | None], values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, keys: list[expr | None] = ..., values: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Set(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elts",) + elts: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class ListComp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elt", "generators") + elt: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class SetComp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elt", "generators") + elt: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class DictComp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("key", "value", "generators") + key: expr + value: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__( + self, key: expr, value: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, key: expr, value: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, key: expr = ..., value: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class GeneratorExp(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elt", "generators") + elt: expr + generators: list[comprehension] + if sys.version_info >= (3, 13): + def __init__(self, elt: expr, generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elt: expr, generators: list[comprehension], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, elt: expr = ..., generators: list[comprehension] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Await(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Yield(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr | None + def __init__(self, value: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class YieldFrom(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Compare(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("left", "ops", "comparators") + left: expr + ops: list[cmpop] + comparators: list[expr] + if sys.version_info >= (3, 13): + def __init__( + self, left: expr, ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, left: expr, ops: list[cmpop], comparators: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, left: expr = ..., ops: list[cmpop] = ..., comparators: list[expr] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Call(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("func", "args", "keywords") + func: expr + args: list[expr] + keywords: list[keyword] + if sys.version_info >= (3, 13): + def __init__( + self, func: expr, args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + def __init__(self, func: expr, args: list[expr], keywords: list[keyword], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, func: expr = ..., args: list[expr] = ..., keywords: list[keyword] = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class FormattedValue(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "conversion", "format_spec") + value: expr + conversion: int + format_spec: expr | None + def __init__(self, value: expr, conversion: int, format_spec: expr | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., conversion: int = ..., format_spec: expr | None = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class JoinedStr(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("values",) + values: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, values: list[expr], **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, values: list[expr] = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Constant(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "kind") + value: Any # None, str, bytes, bool, int, float, complex, Ellipsis + kind: str | None + if sys.version_info < (3, 14): + # Aliases for value, for backwards compatibility + s: Any + n: int | float | complex + + def __init__(self, value: Any, kind: str | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: Any = ..., kind: str | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Attribute(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "attr", "ctx") + value: expr + attr: _Identifier + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, attr: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., attr: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Subscript(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "slice", "ctx") + value: expr + slice: _Slice + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, slice: _Slice, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, value: expr = ..., slice: _Slice = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes] + ) -> Self: ... + +class Starred(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("value", "ctx") + value: expr + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, value: expr, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Name(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("id", "ctx") + id: _Identifier + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + def __init__(self, id: _Identifier, ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, id: _Identifier = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class List(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elts", "ctx") + elts: list[expr] + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class Tuple(expr): + if sys.version_info >= (3, 10): + __match_args__ = ("elts", "ctx") + elts: list[expr] + ctx: expr_context # Not present in Python < 3.13 if not passed to `__init__` + if sys.version_info >= (3, 9): + dims: list[expr] + if sys.version_info >= (3, 13): + def __init__(self, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + else: + def __init__(self, elts: list[expr], ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, elts: list[expr] = ..., ctx: expr_context = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class slice(AST): ... # deprecated and moved to ast.py for >= (3, 9) + +if sys.version_info >= (3, 9): + _Slice: typing_extensions.TypeAlias = expr + _SliceAttributes: typing_extensions.TypeAlias = _Attributes +else: + # alias for use with variables named slice + _Slice: typing_extensions.TypeAlias = slice + + class _SliceAttributes(TypedDict): ... + +class Slice(_Slice): if sys.version_info >= (3, 10): - def visit_Match(self, node: Match) -> Any: ... - def visit_match_case(self, node: match_case) -> Any: ... - def visit_MatchValue(self, node: MatchValue) -> Any: ... - def visit_MatchSequence(self, node: MatchSequence) -> Any: ... - def visit_MatchSingleton(self, node: MatchSingleton) -> Any: ... - def visit_MatchStar(self, node: MatchStar) -> Any: ... - def visit_MatchMapping(self, node: MatchMapping) -> Any: ... - def visit_MatchClass(self, node: MatchClass) -> Any: ... - def visit_MatchAs(self, node: MatchAs) -> Any: ... - def visit_MatchOr(self, node: MatchOr) -> Any: ... + __match_args__ = ("lower", "upper", "step") + lower: expr | None + upper: expr | None + step: expr | None + def __init__( + self, lower: expr | None = None, upper: expr | None = None, step: expr | None = None, **kwargs: Unpack[_SliceAttributes] + ) -> None: ... - if sys.version_info >= (3, 11): - def visit_TryStar(self, node: TryStar) -> Any: ... + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + lower: expr | None = ..., + upper: expr | None = ..., + step: expr | None = ..., + **kwargs: Unpack[_SliceAttributes], + ) -> Self: ... - if sys.version_info >= (3, 12): - def visit_TypeVar(self, node: TypeVar) -> Any: ... - def visit_ParamSpec(self, node: ParamSpec) -> Any: ... - def visit_TypeVarTuple(self, node: TypeVarTuple) -> Any: ... - def visit_TypeAlias(self, node: TypeAlias) -> Any: ... +class ExtSlice(slice): # deprecated and moved to ast.py if sys.version_info >= (3, 9) + dims: list[slice] + def __init__(self, dims: list[slice], **kwargs: Unpack[_SliceAttributes]) -> None: ... - # visit methods for deprecated nodes - def visit_ExtSlice(self, node: ExtSlice) -> Any: ... - def visit_Index(self, node: Index) -> Any: ... - def visit_Suite(self, node: Suite) -> Any: ... - def visit_AugLoad(self, node: AugLoad) -> Any: ... - def visit_AugStore(self, node: AugStore) -> Any: ... - def visit_Param(self, node: Param) -> Any: ... - def visit_Num(self, node: Num) -> Any: ... - def visit_Str(self, node: Str) -> Any: ... - def visit_Bytes(self, node: Bytes) -> Any: ... - def visit_NameConstant(self, node: NameConstant) -> Any: ... - def visit_Ellipsis(self, node: Ellipsis) -> Any: ... +class Index(slice): # deprecated and moved to ast.py if sys.version_info >= (3, 9) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_SliceAttributes]) -> None: ... -class NodeTransformer(NodeVisitor): - def generic_visit(self, node: AST) -> AST: ... - # TODO: Override the visit_* methods with better return types. - # The usual return type is AST | None, but Iterable[AST] - # is also allowed in some cases -- this needs to be mapped. +class expr_context(AST): ... +class AugLoad(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) +class AugStore(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) +class Param(expr_context): ... # deprecated and moved to ast.py if sys.version_info >= (3, 9) + +class Suite(mod): # deprecated and moved to ast.py if sys.version_info >= (3, 9) + body: list[stmt] + def __init__(self, body: list[stmt]) -> None: ... + +class Load(expr_context): ... +class Store(expr_context): ... +class Del(expr_context): ... +class boolop(AST): ... +class And(boolop): ... +class Or(boolop): ... +class operator(AST): ... +class Add(operator): ... +class Sub(operator): ... +class Mult(operator): ... +class MatMult(operator): ... +class Div(operator): ... +class Mod(operator): ... +class Pow(operator): ... +class LShift(operator): ... +class RShift(operator): ... +class BitOr(operator): ... +class BitXor(operator): ... +class BitAnd(operator): ... +class FloorDiv(operator): ... +class unaryop(AST): ... +class Invert(unaryop): ... +class Not(unaryop): ... +class UAdd(unaryop): ... +class USub(unaryop): ... +class cmpop(AST): ... +class Eq(cmpop): ... +class NotEq(cmpop): ... +class Lt(cmpop): ... +class LtE(cmpop): ... +class Gt(cmpop): ... +class GtE(cmpop): ... +class Is(cmpop): ... +class IsNot(cmpop): ... +class In(cmpop): ... +class NotIn(cmpop): ... + +class comprehension(AST): + if sys.version_info >= (3, 10): + __match_args__ = ("target", "iter", "ifs", "is_async") + target: expr + iter: expr + ifs: list[expr] + is_async: int + if sys.version_info >= (3, 13): + @overload + def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... + @overload + def __init__(self, target: expr, iter: expr, ifs: list[expr] = ..., *, is_async: int) -> None: ... + else: + def __init__(self, target: expr, iter: expr, ifs: list[expr], is_async: int) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, target: expr = ..., iter: expr = ..., ifs: list[expr] = ..., is_async: int = ...) -> Self: ... + +class excepthandler(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + def __init__(self, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int | None = ..., end_col_offset: int | None = ... + ) -> Self: ... + +class ExceptHandler(excepthandler): + if sys.version_info >= (3, 10): + __match_args__ = ("type", "name", "body") + type: expr | None + name: _Identifier | None + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__( + self, type: expr | None = None, name: _Identifier | None = None, body: list[stmt] = ..., **kwargs: Unpack[_Attributes] + ) -> None: ... + else: + @overload + def __init__( + self, type: expr | None, name: _Identifier | None, body: list[stmt], **kwargs: Unpack[_Attributes] + ) -> None: ... + @overload + def __init__( + self, type: expr | None = None, name: _Identifier | None = None, *, body: list[stmt], **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + type: expr | None = ..., + name: _Identifier | None = ..., + body: list[stmt] = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class arguments(AST): + if sys.version_info >= (3, 10): + __match_args__ = ("posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults") + posonlyargs: list[arg] + args: list[arg] + vararg: arg | None + kwonlyargs: list[arg] + kw_defaults: list[expr | None] + kwarg: arg | None + defaults: list[expr] + if sys.version_info >= (3, 13): + def __init__( + self, + posonlyargs: list[arg] = ..., + args: list[arg] = ..., + vararg: arg | None = None, + kwonlyargs: list[arg] = ..., + kw_defaults: list[expr | None] = ..., + kwarg: arg | None = None, + defaults: list[expr] = ..., + ) -> None: ... + else: + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None, + defaults: list[expr], + ) -> None: ... + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None = None, + *, + defaults: list[expr], + ) -> None: ... + @overload + def __init__( + self, + posonlyargs: list[arg], + args: list[arg], + vararg: arg | None = None, + *, + kwonlyargs: list[arg], + kw_defaults: list[expr | None], + kwarg: arg | None = None, + defaults: list[expr], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + posonlyargs: list[arg] = ..., + args: list[arg] = ..., + vararg: arg | None = ..., + kwonlyargs: list[arg] = ..., + kw_defaults: list[expr | None] = ..., + kwarg: arg | None = ..., + defaults: list[expr] = ..., + ) -> Self: ... + +class arg(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + if sys.version_info >= (3, 10): + __match_args__ = ("arg", "annotation", "type_comment") + arg: _Identifier + annotation: expr | None + type_comment: str | None + def __init__( + self, arg: _Identifier, annotation: expr | None = None, type_comment: str | None = None, **kwargs: Unpack[_Attributes] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + arg: _Identifier = ..., + annotation: expr | None = ..., + type_comment: str | None = ..., + **kwargs: Unpack[_Attributes], + ) -> Self: ... + +class keyword(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + if sys.version_info >= (3, 10): + __match_args__ = ("arg", "value") + arg: _Identifier | None + value: expr + @overload + def __init__(self, arg: _Identifier | None, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + @overload + def __init__(self, arg: _Identifier | None = None, *, value: expr, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, arg: _Identifier | None = ..., value: expr = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class alias(AST): + lineno: int + col_offset: int + end_lineno: int | None + end_col_offset: int | None + if sys.version_info >= (3, 10): + __match_args__ = ("name", "asname") + name: str + asname: _Identifier | None + def __init__(self, name: str, asname: _Identifier | None = None, **kwargs: Unpack[_Attributes]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, name: str = ..., asname: _Identifier | None = ..., **kwargs: Unpack[_Attributes]) -> Self: ... + +class withitem(AST): + if sys.version_info >= (3, 10): + __match_args__ = ("context_expr", "optional_vars") + context_expr: expr + optional_vars: expr | None + def __init__(self, context_expr: expr, optional_vars: expr | None = None) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, context_expr: expr = ..., optional_vars: expr | None = ...) -> Self: ... + +if sys.version_info >= (3, 10): + class match_case(AST): + __match_args__ = ("pattern", "guard", "body") + pattern: _Pattern + guard: expr | None + body: list[stmt] + if sys.version_info >= (3, 13): + def __init__(self, pattern: _Pattern, guard: expr | None = None, body: list[stmt] = ...) -> None: ... + else: + @overload + def __init__(self, pattern: _Pattern, guard: expr | None, body: list[stmt]) -> None: ... + @overload + def __init__(self, pattern: _Pattern, guard: expr | None = None, *, body: list[stmt]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, pattern: _Pattern = ..., guard: expr | None = ..., body: list[stmt] = ...) -> Self: ... + + class pattern(AST): + lineno: int + col_offset: int + end_lineno: int + end_col_offset: int + def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, lineno: int = ..., col_offset: int = ..., end_lineno: int = ..., end_col_offset: int = ... + ) -> Self: ... + + # Without the alias, Pyright complains variables named pattern are recursively defined + _Pattern: typing_extensions.TypeAlias = pattern + + class MatchValue(pattern): + __match_args__ = ("value",) + value: expr + def __init__(self, value: expr, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: expr = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchSingleton(pattern): + __match_args__ = ("value",) + value: Literal[True, False] | None + def __init__(self, value: Literal[True, False] | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, value: Literal[True, False] | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchSequence(pattern): + __match_args__ = ("patterns",) + patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchMapping(pattern): + __match_args__ = ("keys", "patterns", "rest") + keys: list[expr] + patterns: list[pattern] + rest: _Identifier | None + if sys.version_info >= (3, 13): + def __init__( + self, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: _Identifier | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, + keys: list[expr], + patterns: list[pattern], + rest: _Identifier | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + keys: list[expr] = ..., + patterns: list[pattern] = ..., + rest: _Identifier | None = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + + class MatchClass(pattern): + __match_args__ = ("cls", "patterns", "kwd_attrs", "kwd_patterns") + cls: expr + patterns: list[pattern] + kwd_attrs: list[_Identifier] + kwd_patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__( + self, + cls: expr, + patterns: list[pattern] = ..., + kwd_attrs: list[_Identifier] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__( + self, + cls: expr, + patterns: list[pattern], + kwd_attrs: list[_Identifier], + kwd_patterns: list[pattern], + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + cls: expr = ..., + patterns: list[pattern] = ..., + kwd_attrs: list[_Identifier] = ..., + kwd_patterns: list[pattern] = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + + class MatchStar(pattern): + __match_args__ = ("name",) + name: _Identifier | None + def __init__(self, name: _Identifier | None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class MatchAs(pattern): + __match_args__ = ("pattern", "name") + pattern: _Pattern | None + name: _Identifier | None + def __init__( + self, pattern: _Pattern | None = None, name: _Identifier | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, pattern: _Pattern | None = ..., name: _Identifier | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + + class MatchOr(pattern): + __match_args__ = ("patterns",) + patterns: list[pattern] + if sys.version_info >= (3, 13): + def __init__(self, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> None: ... + else: + def __init__(self, patterns: list[pattern], **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, patterns: list[pattern] = ..., **kwargs: Unpack[_Attributes[int]]) -> Self: ... + +class type_ignore(AST): ... + +class TypeIgnore(type_ignore): + if sys.version_info >= (3, 10): + __match_args__ = ("lineno", "tag") + lineno: int + tag: str + def __init__(self, lineno: int, tag: str) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, *, lineno: int = ..., tag: str = ...) -> Self: ... + +if sys.version_info >= (3, 12): + class type_param(AST): + lineno: int + col_offset: int + end_lineno: int + end_col_offset: int + def __init__(self, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__(self, **kwargs: Unpack[_Attributes[int]]) -> Self: ... + + class TypeVar(type_param): + if sys.version_info >= (3, 13): + __match_args__ = ("name", "bound", "default_value") + else: + __match_args__ = ("name", "bound") + name: _Identifier + bound: expr | None + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, + name: _Identifier, + bound: expr | None = None, + default_value: expr | None = None, + **kwargs: Unpack[_Attributes[int]], + ) -> None: ... + else: + def __init__(self, name: _Identifier, bound: expr | None = None, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, + *, + name: _Identifier = ..., + bound: expr | None = ..., + default_value: expr | None = ..., + **kwargs: Unpack[_Attributes[int]], + ) -> Self: ... + + class ParamSpec(type_param): + if sys.version_info >= (3, 13): + __match_args__ = ("name", "default_value") + else: + __match_args__ = ("name",) + name: _Identifier + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + + class TypeVarTuple(type_param): + if sys.version_info >= (3, 13): + __match_args__ = ("name", "default_value") + else: + __match_args__ = ("name",) + name: _Identifier + if sys.version_info >= (3, 13): + default_value: expr | None + def __init__( + self, name: _Identifier, default_value: expr | None = None, **kwargs: Unpack[_Attributes[int]] + ) -> None: ... + else: + def __init__(self, name: _Identifier, **kwargs: Unpack[_Attributes[int]]) -> None: ... + + if sys.version_info >= (3, 14): + def __replace__( + self, *, name: _Identifier = ..., default_value: expr | None = ..., **kwargs: Unpack[_Attributes[int]] + ) -> Self: ... + +class _ABC(type): + if sys.version_info >= (3, 9): + def __init__(cls, *args: Unused) -> None: ... + +if sys.version_info < (3, 14): + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Num(Constant, metaclass=_ABC): + value: int | float | complex + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Str(Constant, metaclass=_ABC): + value: str + # Aliases for value, for backwards compatibility + s: str + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Bytes(Constant, metaclass=_ABC): + value: bytes + # Aliases for value, for backwards compatibility + s: bytes + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class NameConstant(Constant, metaclass=_ABC): ... + + @deprecated("Replaced by ast.Constant; removed in Python 3.14") + class Ellipsis(Constant, metaclass=_ABC): ... + +# everything below here is defined in ast.py _T = _TypeVar("_T", bound=AST) @@ -331,10 +1865,7 @@ else: feature_version: None | int | tuple[int, int] = None, ) -> AST: ... -if sys.version_info >= (3, 9): - def unparse(ast_obj: AST) -> str: ... - -def copy_location(new_node: _T, old_node: AST) -> _T: ... +def literal_eval(node_or_string: str | AST) -> Any: ... if sys.version_info >= (3, 13): def dump( @@ -354,17 +1885,165 @@ elif sys.version_info >= (3, 9): else: def dump(node: AST, annotate_fields: bool = True, include_attributes: bool = False) -> str: ... +def copy_location(new_node: _T, old_node: AST) -> _T: ... def fix_missing_locations(node: _T) -> _T: ... -def get_docstring(node: AsyncFunctionDef | FunctionDef | ClassDef | Module, clean: bool = True) -> str | None: ... def increment_lineno(node: _T, n: int = 1) -> _T: ... -def iter_child_nodes(node: AST) -> Iterator[AST]: ... def iter_fields(node: AST) -> Iterator[tuple[str, Any]]: ... -def literal_eval(node_or_string: str | AST) -> Any: ... +def iter_child_nodes(node: AST) -> Iterator[AST]: ... +def get_docstring(node: AsyncFunctionDef | FunctionDef | ClassDef | Module, clean: bool = True) -> str | None: ... def get_source_segment(source: str, node: AST, *, padded: bool = False) -> str | None: ... def walk(node: AST) -> Iterator[AST]: ... -if sys.version_info >= (3, 9): - def main() -> None: ... - if sys.version_info >= (3, 14): def compare(left: AST, right: AST, /, *, compare_attributes: bool = False) -> bool: ... + +class NodeVisitor: + def visit(self, node: AST) -> Any: ... + def generic_visit(self, node: AST) -> Any: ... + def visit_Module(self, node: Module) -> Any: ... + def visit_Interactive(self, node: Interactive) -> Any: ... + def visit_Expression(self, node: Expression) -> Any: ... + def visit_FunctionDef(self, node: FunctionDef) -> Any: ... + def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> Any: ... + def visit_ClassDef(self, node: ClassDef) -> Any: ... + def visit_Return(self, node: Return) -> Any: ... + def visit_Delete(self, node: Delete) -> Any: ... + def visit_Assign(self, node: Assign) -> Any: ... + def visit_AugAssign(self, node: AugAssign) -> Any: ... + def visit_AnnAssign(self, node: AnnAssign) -> Any: ... + def visit_For(self, node: For) -> Any: ... + def visit_AsyncFor(self, node: AsyncFor) -> Any: ... + def visit_While(self, node: While) -> Any: ... + def visit_If(self, node: If) -> Any: ... + def visit_With(self, node: With) -> Any: ... + def visit_AsyncWith(self, node: AsyncWith) -> Any: ... + def visit_Raise(self, node: Raise) -> Any: ... + def visit_Try(self, node: Try) -> Any: ... + def visit_Assert(self, node: Assert) -> Any: ... + def visit_Import(self, node: Import) -> Any: ... + def visit_ImportFrom(self, node: ImportFrom) -> Any: ... + def visit_Global(self, node: Global) -> Any: ... + def visit_Nonlocal(self, node: Nonlocal) -> Any: ... + def visit_Expr(self, node: Expr) -> Any: ... + def visit_Pass(self, node: Pass) -> Any: ... + def visit_Break(self, node: Break) -> Any: ... + def visit_Continue(self, node: Continue) -> Any: ... + def visit_Slice(self, node: Slice) -> Any: ... + def visit_BoolOp(self, node: BoolOp) -> Any: ... + def visit_BinOp(self, node: BinOp) -> Any: ... + def visit_UnaryOp(self, node: UnaryOp) -> Any: ... + def visit_Lambda(self, node: Lambda) -> Any: ... + def visit_IfExp(self, node: IfExp) -> Any: ... + def visit_Dict(self, node: Dict) -> Any: ... + def visit_Set(self, node: Set) -> Any: ... + def visit_ListComp(self, node: ListComp) -> Any: ... + def visit_SetComp(self, node: SetComp) -> Any: ... + def visit_DictComp(self, node: DictComp) -> Any: ... + def visit_GeneratorExp(self, node: GeneratorExp) -> Any: ... + def visit_Await(self, node: Await) -> Any: ... + def visit_Yield(self, node: Yield) -> Any: ... + def visit_YieldFrom(self, node: YieldFrom) -> Any: ... + def visit_Compare(self, node: Compare) -> Any: ... + def visit_Call(self, node: Call) -> Any: ... + def visit_FormattedValue(self, node: FormattedValue) -> Any: ... + def visit_JoinedStr(self, node: JoinedStr) -> Any: ... + def visit_Constant(self, node: Constant) -> Any: ... + def visit_NamedExpr(self, node: NamedExpr) -> Any: ... + def visit_TypeIgnore(self, node: TypeIgnore) -> Any: ... + def visit_Attribute(self, node: Attribute) -> Any: ... + def visit_Subscript(self, node: Subscript) -> Any: ... + def visit_Starred(self, node: Starred) -> Any: ... + def visit_Name(self, node: Name) -> Any: ... + def visit_List(self, node: List) -> Any: ... + def visit_Tuple(self, node: Tuple) -> Any: ... + def visit_Del(self, node: Del) -> Any: ... + def visit_Load(self, node: Load) -> Any: ... + def visit_Store(self, node: Store) -> Any: ... + def visit_And(self, node: And) -> Any: ... + def visit_Or(self, node: Or) -> Any: ... + def visit_Add(self, node: Add) -> Any: ... + def visit_BitAnd(self, node: BitAnd) -> Any: ... + def visit_BitOr(self, node: BitOr) -> Any: ... + def visit_BitXor(self, node: BitXor) -> Any: ... + def visit_Div(self, node: Div) -> Any: ... + def visit_FloorDiv(self, node: FloorDiv) -> Any: ... + def visit_LShift(self, node: LShift) -> Any: ... + def visit_Mod(self, node: Mod) -> Any: ... + def visit_Mult(self, node: Mult) -> Any: ... + def visit_MatMult(self, node: MatMult) -> Any: ... + def visit_Pow(self, node: Pow) -> Any: ... + def visit_RShift(self, node: RShift) -> Any: ... + def visit_Sub(self, node: Sub) -> Any: ... + def visit_Invert(self, node: Invert) -> Any: ... + def visit_Not(self, node: Not) -> Any: ... + def visit_UAdd(self, node: UAdd) -> Any: ... + def visit_USub(self, node: USub) -> Any: ... + def visit_Eq(self, node: Eq) -> Any: ... + def visit_Gt(self, node: Gt) -> Any: ... + def visit_GtE(self, node: GtE) -> Any: ... + def visit_In(self, node: In) -> Any: ... + def visit_Is(self, node: Is) -> Any: ... + def visit_IsNot(self, node: IsNot) -> Any: ... + def visit_Lt(self, node: Lt) -> Any: ... + def visit_LtE(self, node: LtE) -> Any: ... + def visit_NotEq(self, node: NotEq) -> Any: ... + def visit_NotIn(self, node: NotIn) -> Any: ... + def visit_comprehension(self, node: comprehension) -> Any: ... + def visit_ExceptHandler(self, node: ExceptHandler) -> Any: ... + def visit_arguments(self, node: arguments) -> Any: ... + def visit_arg(self, node: arg) -> Any: ... + def visit_keyword(self, node: keyword) -> Any: ... + def visit_alias(self, node: alias) -> Any: ... + def visit_withitem(self, node: withitem) -> Any: ... + if sys.version_info >= (3, 10): + def visit_Match(self, node: Match) -> Any: ... + def visit_match_case(self, node: match_case) -> Any: ... + def visit_MatchValue(self, node: MatchValue) -> Any: ... + def visit_MatchSequence(self, node: MatchSequence) -> Any: ... + def visit_MatchSingleton(self, node: MatchSingleton) -> Any: ... + def visit_MatchStar(self, node: MatchStar) -> Any: ... + def visit_MatchMapping(self, node: MatchMapping) -> Any: ... + def visit_MatchClass(self, node: MatchClass) -> Any: ... + def visit_MatchAs(self, node: MatchAs) -> Any: ... + def visit_MatchOr(self, node: MatchOr) -> Any: ... + + if sys.version_info >= (3, 11): + def visit_TryStar(self, node: TryStar) -> Any: ... + + if sys.version_info >= (3, 12): + def visit_TypeVar(self, node: TypeVar) -> Any: ... + def visit_ParamSpec(self, node: ParamSpec) -> Any: ... + def visit_TypeVarTuple(self, node: TypeVarTuple) -> Any: ... + def visit_TypeAlias(self, node: TypeAlias) -> Any: ... + + # visit methods for deprecated nodes + def visit_ExtSlice(self, node: ExtSlice) -> Any: ... + def visit_Index(self, node: Index) -> Any: ... + def visit_Suite(self, node: Suite) -> Any: ... + def visit_AugLoad(self, node: AugLoad) -> Any: ... + def visit_AugStore(self, node: AugStore) -> Any: ... + def visit_Param(self, node: Param) -> Any: ... + + if sys.version_info < (3, 14): + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Num(self, node: Num) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Str(self, node: Str) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Bytes(self, node: Bytes) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_NameConstant(self, node: NameConstant) -> Any: ... # type: ignore[deprecated] + @deprecated("Replaced by visit_Constant; removed in Python 3.14") + def visit_Ellipsis(self, node: Ellipsis) -> Any: ... # type: ignore[deprecated] + +class NodeTransformer(NodeVisitor): + def generic_visit(self, node: AST) -> AST: ... + # TODO: Override the visit_* methods with better return types. + # The usual return type is AST | None, but Iterable[AST] + # is also allowed in some cases -- this needs to be mapped. + +if sys.version_info >= (3, 9): + def unparse(ast_obj: AST) -> str: ... + +if sys.version_info >= (3, 9): + def main() -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index 112cfeefa8f2d..cba2c77995284 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -49,6 +49,10 @@ class Server(AbstractServer): ssl_handshake_timeout: float | None, ) -> None: ... + if sys.version_info >= (3, 13): + def close_clients(self) -> None: ... + def abort_clients(self) -> None: ... + def get_loop(self) -> AbstractEventLoop: ... def is_serving(self) -> bool: ... async def start_serving(self) -> None: ... @@ -222,7 +226,9 @@ class BaseEventLoop(AbstractEventLoop): happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - if sys.version_info >= (3, 11): + + if sys.version_info >= (3, 13): + # 3.13 added `keep_alive`. @overload async def create_server( self, @@ -237,6 +243,7 @@ class BaseEventLoop(AbstractEventLoop): ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, @@ -255,30 +262,48 @@ class BaseEventLoop(AbstractEventLoop): ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... - async def start_tls( + elif sys.version_info >= (3, 11): + @overload + async def create_server( self, - transport: BaseTransport, - protocol: BaseProtocol, - sslcontext: ssl.SSLContext, + protocol_factory: _ProtocolFactory, + host: str | Sequence[str] | None = None, + port: int = ..., *, - server_side: bool = False, - server_hostname: str | None = None, + family: int = ..., + flags: int = ..., + sock: None = None, + backlog: int = 100, + ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> Transport | None: ... - async def connect_accepted_socket( + start_serving: bool = True, + ) -> Server: ... + @overload + async def create_server( self, - protocol_factory: Callable[[], _ProtocolT], - sock: socket, + protocol_factory: _ProtocolFactory, + host: None = None, + port: None = None, *, + family: int = ..., + flags: int = ..., + sock: socket = ..., + backlog: int = 100, ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> tuple[Transport, _ProtocolT]: ... + start_serving: bool = True, + ) -> Server: ... else: @overload async def create_server( @@ -314,6 +339,29 @@ class BaseEventLoop(AbstractEventLoop): ssl_handshake_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + + if sys.version_info >= (3, 11): + async def start_tls( + self, + transport: BaseTransport, + protocol: BaseProtocol, + sslcontext: ssl.SSLContext, + *, + server_side: bool = False, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> Transport | None: ... + async def connect_accepted_socket( + self, + protocol_factory: Callable[[], _ProtocolT], + sock: socket, + *, + ssl: _SSLContext = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> tuple[Transport, _ProtocolT]: ... + else: async def start_tls( self, transport: BaseTransport, diff --git a/mypy/typeshed/stdlib/asyncio/base_futures.pyi b/mypy/typeshed/stdlib/asyncio/base_futures.pyi index 2317662009349..55d2fbdbdb627 100644 --- a/mypy/typeshed/stdlib/asyncio/base_futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_futures.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable, Sequence from contextvars import Context -from typing import Any, Literal +from typing import Any, Final from . import futures @@ -11,9 +11,9 @@ __all__ = () # That's why the import order is reversed. from .futures import isfuture as isfuture -_PENDING: Literal["PENDING"] # undocumented -_CANCELLED: Literal["CANCELLED"] # undocumented -_FINISHED: Literal["FINISHED"] # undocumented +_PENDING: Final = "PENDING" # undocumented +_CANCELLED: Final = "CANCELLED" # undocumented +_FINISHED: Final = "FINISHED" # undocumented def _format_callbacks(cb: Sequence[tuple[Callable[[futures.Future[Any]], None], Context]]) -> str: ... # undocumented def _future_repr_info(future: futures.Future[Any]) -> list[str]: ... # undocumented diff --git a/mypy/typeshed/stdlib/asyncio/constants.pyi b/mypy/typeshed/stdlib/asyncio/constants.pyi index 7759a28449530..5c6456b0e9c04 100644 --- a/mypy/typeshed/stdlib/asyncio/constants.pyi +++ b/mypy/typeshed/stdlib/asyncio/constants.pyi @@ -1,18 +1,18 @@ import enum import sys -from typing import Literal +from typing import Final -LOG_THRESHOLD_FOR_CONNLOST_WRITES: Literal[5] -ACCEPT_RETRY_DELAY: Literal[1] -DEBUG_STACK_DEPTH: Literal[10] +LOG_THRESHOLD_FOR_CONNLOST_WRITES: Final = 5 +ACCEPT_RETRY_DELAY: Final = 1 +DEBUG_STACK_DEPTH: Final = 10 SSL_HANDSHAKE_TIMEOUT: float -SENDFILE_FALLBACK_READBUFFER_SIZE: Literal[262144] +SENDFILE_FALLBACK_READBUFFER_SIZE: Final = 262144 if sys.version_info >= (3, 11): SSL_SHUTDOWN_TIMEOUT: float - FLOW_CONTROL_HIGH_WATER_SSL_READ: Literal[256] - FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Literal[512] + FLOW_CONTROL_HIGH_WATER_SSL_READ: Final = 256 + FLOW_CONTROL_HIGH_WATER_SSL_WRITE: Final = 512 if sys.version_info >= (3, 12): - THREAD_JOIN_TIMEOUT: Literal[300] + THREAD_JOIN_TIMEOUT: Final = 300 class _SendfileMode(enum.Enum): UNSUPPORTED = 1 diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index c0345eb1b5b54..ead64070671fc 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -1,5 +1,11 @@ import ssl import sys +from _asyncio import ( + _get_running_loop as _get_running_loop, + _set_running_loop as _set_running_loop, + get_event_loop as get_event_loop, + get_running_loop as get_running_loop, +) from _typeshed import FileDescriptorLike, ReadableBuffer, StrPath, Unused, WriteableBuffer from abc import ABCMeta, abstractmethod from collections.abc import Callable, Sequence @@ -16,23 +22,40 @@ from .tasks import Task from .transports import BaseTransport, DatagramTransport, ReadTransport, SubprocessTransport, Transport, WriteTransport from .unix_events import AbstractChildWatcher -__all__ = ( - "AbstractEventLoopPolicy", - "AbstractEventLoop", - "AbstractServer", - "Handle", - "TimerHandle", - "get_event_loop_policy", - "set_event_loop_policy", - "get_event_loop", - "set_event_loop", - "new_event_loop", - "get_child_watcher", - "set_child_watcher", - "_set_running_loop", - "get_running_loop", - "_get_running_loop", -) +if sys.version_info >= (3, 14): + __all__ = ( + "AbstractEventLoopPolicy", + "AbstractEventLoop", + "AbstractServer", + "Handle", + "TimerHandle", + "get_event_loop_policy", + "set_event_loop_policy", + "get_event_loop", + "set_event_loop", + "new_event_loop", + "_set_running_loop", + "get_running_loop", + "_get_running_loop", + ) +else: + __all__ = ( + "AbstractEventLoopPolicy", + "AbstractEventLoop", + "AbstractServer", + "Handle", + "TimerHandle", + "get_event_loop_policy", + "set_event_loop_policy", + "get_event_loop", + "set_event_loop", + "new_event_loop", + "get_child_watcher", + "set_child_watcher", + "_set_running_loop", + "get_running_loop", + "_get_running_loop", + ) _T = TypeVar("_T") _Ts = TypeVarTuple("_Ts") @@ -77,6 +100,12 @@ class TimerHandle(Handle): class AbstractServer: @abstractmethod def close(self) -> None: ... + if sys.version_info >= (3, 13): + @abstractmethod + def close_clients(self) -> None: ... + @abstractmethod + def abort_clients(self) -> None: ... + async def __aenter__(self) -> Self: ... async def __aexit__(self, *exc: Unused) -> None: ... @abstractmethod @@ -255,7 +284,9 @@ class AbstractEventLoop: happy_eyeballs_delay: float | None = None, interleave: int | None = None, ) -> tuple[Transport, _ProtocolT]: ... - if sys.version_info >= (3, 11): + + if sys.version_info >= (3, 13): + # 3.13 added `keep_alive`. @overload @abstractmethod async def create_server( @@ -271,6 +302,7 @@ class AbstractEventLoop: ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, @@ -290,30 +322,46 @@ class AbstractEventLoop: ssl: _SSLContext = None, reuse_address: bool | None = None, reuse_port: bool | None = None, + keep_alive: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + elif sys.version_info >= (3, 11): + @overload @abstractmethod - async def start_tls( + async def create_server( self, - transport: WriteTransport, - protocol: BaseProtocol, - sslcontext: ssl.SSLContext, + protocol_factory: _ProtocolFactory, + host: str | Sequence[str] | None = None, + port: int = ..., *, - server_side: bool = False, - server_hostname: str | None = None, + family: int = ..., + flags: int = ..., + sock: None = None, + backlog: int = 100, + ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, - ) -> Transport | None: ... - async def create_unix_server( + start_serving: bool = True, + ) -> Server: ... + @overload + @abstractmethod + async def create_server( self, protocol_factory: _ProtocolFactory, - path: StrPath | None = None, + host: None = None, + port: None = None, *, - sock: socket | None = None, + family: int = ..., + flags: int = ..., + sock: socket = ..., backlog: int = 100, ssl: _SSLContext = None, + reuse_address: bool | None = None, + reuse_port: bool | None = None, ssl_handshake_timeout: float | None = None, ssl_shutdown_timeout: float | None = None, start_serving: bool = True, @@ -355,6 +403,33 @@ class AbstractEventLoop: ssl_handshake_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + + if sys.version_info >= (3, 11): + @abstractmethod + async def start_tls( + self, + transport: WriteTransport, + protocol: BaseProtocol, + sslcontext: ssl.SSLContext, + *, + server_side: bool = False, + server_hostname: str | None = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + ) -> Transport | None: ... + async def create_unix_server( + self, + protocol_factory: _ProtocolFactory, + path: StrPath | None = None, + *, + sock: socket | None = None, + backlog: int = 100, + ssl: _SSLContext = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + start_serving: bool = True, + ) -> Server: ... + else: @abstractmethod async def start_tls( self, @@ -377,6 +452,7 @@ class AbstractEventLoop: ssl_handshake_timeout: float | None = None, start_serving: bool = True, ) -> Server: ... + if sys.version_info >= (3, 11): async def connect_accepted_socket( self, @@ -541,18 +617,19 @@ class AbstractEventLoopPolicy: @abstractmethod def new_event_loop(self) -> AbstractEventLoop: ... # Child processes handling (Unix only). - if sys.version_info >= (3, 12): - @abstractmethod - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def get_child_watcher(self) -> AbstractChildWatcher: ... - @abstractmethod - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... - else: - @abstractmethod - def get_child_watcher(self) -> AbstractChildWatcher: ... - @abstractmethod - def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... + if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @abstractmethod + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher(self) -> AbstractChildWatcher: ... + @abstractmethod + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... + else: + @abstractmethod + def get_child_watcher(self) -> AbstractChildWatcher: ... + @abstractmethod + def set_child_watcher(self, watcher: AbstractChildWatcher) -> None: ... class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta): def get_event_loop(self) -> AbstractEventLoop: ... @@ -561,20 +638,16 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy, metaclass=ABCMeta): def get_event_loop_policy() -> AbstractEventLoopPolicy: ... def set_event_loop_policy(policy: AbstractEventLoopPolicy | None) -> None: ... -def get_event_loop() -> AbstractEventLoop: ... def set_event_loop(loop: AbstractEventLoop | None) -> None: ... def new_event_loop() -> AbstractEventLoop: ... -if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def get_child_watcher() -> AbstractChildWatcher: ... - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... - -else: - def get_child_watcher() -> AbstractChildWatcher: ... - def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... +if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher() -> AbstractChildWatcher: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... -def _set_running_loop(loop: AbstractEventLoop | None, /) -> None: ... -def _get_running_loop() -> AbstractEventLoop: ... -def get_running_loop() -> AbstractEventLoop: ... + else: + def get_child_watcher() -> AbstractChildWatcher: ... + def set_child_watcher(watcher: AbstractChildWatcher) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi index 1c78dff3948a4..41505b14cd087 100644 --- a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi +++ b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi @@ -1,4 +1,5 @@ import functools +import sys import traceback from collections.abc import Iterable from types import FrameType, FunctionType @@ -14,7 +15,17 @@ _FuncType: TypeAlias = FunctionType | _HasWrapper | functools.partial[Any] | fun def _get_function_source(func: _FuncType) -> tuple[str, int]: ... @overload def _get_function_source(func: object) -> tuple[str, int] | None: ... -def _format_callback_source(func: object, args: Iterable[Any]) -> str: ... -def _format_args_and_kwargs(args: Iterable[Any], kwargs: dict[str, Any]) -> str: ... -def _format_callback(func: object, args: Iterable[Any], kwargs: dict[str, Any], suffix: str = "") -> str: ... + +if sys.version_info >= (3, 13): + def _format_callback_source(func: object, args: Iterable[Any], *, debug: bool = False) -> str: ... + def _format_args_and_kwargs(args: Iterable[Any], kwargs: dict[str, Any], *, debug: bool = False) -> str: ... + def _format_callback( + func: object, args: Iterable[Any], kwargs: dict[str, Any], *, debug: bool = False, suffix: str = "" + ) -> str: ... + +else: + def _format_callback_source(func: object, args: Iterable[Any]) -> str: ... + def _format_args_and_kwargs(args: Iterable[Any], kwargs: dict[str, Any]) -> str: ... + def _format_callback(func: object, args: Iterable[Any], kwargs: dict[str, Any], suffix: str = "") -> str: ... + def extract_stack(f: FrameType | None = None, limit: int | None = None) -> traceback.StackSummary: ... diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index e19fd53f33111..28e6ca8c86a33 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -1,15 +1,10 @@ -import sys -from collections.abc import Awaitable, Callable, Generator, Iterable +from _asyncio import Future as Future from concurrent.futures._base import Future as _ConcurrentFuture -from contextvars import Context -from typing import Any, Literal, TypeVar -from typing_extensions import Self, TypeIs +from typing import Any, TypeVar +from typing_extensions import TypeIs from .events import AbstractEventLoop -if sys.version_info >= (3, 9): - from types import GenericAlias - __all__ = ("Future", "wrap_future", "isfuture") _T = TypeVar("_T") @@ -18,40 +13,4 @@ _T = TypeVar("_T") # but it leads to circular import error in pytype tool. # That's why the import order is reversed. def isfuture(obj: object) -> TypeIs[Future[Any]]: ... - -class Future(Awaitable[_T], Iterable[_T]): - _state: str - @property - def _exception(self) -> BaseException | None: ... - _blocking: bool - @property - def _log_traceback(self) -> bool: ... - @_log_traceback.setter - def _log_traceback(self, val: Literal[False]) -> None: ... - _asyncio_future_blocking: bool # is a part of duck-typing contract for `Future` - def __init__(self, *, loop: AbstractEventLoop | None = ...) -> None: ... - def __del__(self) -> None: ... - def get_loop(self) -> AbstractEventLoop: ... - @property - def _callbacks(self) -> list[tuple[Callable[[Self], Any], Context]]: ... - def add_done_callback(self, fn: Callable[[Self], object], /, *, context: Context | None = None) -> None: ... - if sys.version_info >= (3, 9): - def cancel(self, msg: Any | None = None) -> bool: ... - else: - def cancel(self) -> bool: ... - - def cancelled(self) -> bool: ... - def done(self) -> bool: ... - def result(self) -> _T: ... - def exception(self) -> BaseException | None: ... - def remove_done_callback(self, fn: Callable[[Self], object], /) -> int: ... - def set_result(self, result: _T, /) -> None: ... - def set_exception(self, exception: type | BaseException, /) -> None: ... - def __iter__(self) -> Generator[Any, None, _T]: ... - def __await__(self) -> Generator[Any, None, _T]: ... - @property - def _loop(self) -> AbstractEventLoop: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - def wrap_future(future: _ConcurrentFuture[_T] | Future[_T], *, loop: AbstractEventLoop | None = None) -> Future[_T]: ... diff --git a/mypy/typeshed/stdlib/asyncio/queues.pyi b/mypy/typeshed/stdlib/asyncio/queues.pyi index 1d8f80f4c3881..895205aa95197 100644 --- a/mypy/typeshed/stdlib/asyncio/queues.pyi +++ b/mypy/typeshed/stdlib/asyncio/queues.pyi @@ -10,13 +10,20 @@ if sys.version_info >= (3, 10): else: _LoopBoundMixin = object -__all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty") - class QueueEmpty(Exception): ... class QueueFull(Exception): ... +if sys.version_info >= (3, 13): + __all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty", "QueueShutDown") + +else: + __all__ = ("Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty") + _T = TypeVar("_T") +if sys.version_info >= (3, 13): + class QueueShutDown(Exception): ... + # If Generic[_T] is last and _LoopBoundMixin is object, pyright is unhappy. # We can remove the noqa pragma when dropping 3.9 support. class Queue(Generic[_T], _LoopBoundMixin): # noqa: Y059 @@ -42,6 +49,8 @@ class Queue(Generic[_T], _LoopBoundMixin): # noqa: Y059 def task_done(self) -> None: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, type: Any, /) -> GenericAlias: ... + if sys.version_info >= (3, 13): + def shutdown(self, immediate: bool = False) -> None: ... class PriorityQueue(Queue[_T]): ... class LifoQueue(Queue[_T]): ... diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index e904d7395cdc8..ded1933dd6597 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -3,7 +3,7 @@ import sys from collections import deque from collections.abc import Callable from enum import Enum -from typing import Any, ClassVar, Literal +from typing import Any, ClassVar, Final, Literal from typing_extensions import TypeAlias from . import constants, events, futures, protocols, transports @@ -29,10 +29,10 @@ if sys.version_info >= (3, 11): def add_flowcontrol_defaults(high: int | None, low: int | None, kb: int) -> tuple[int, int]: ... else: - _UNWRAPPED: Literal["UNWRAPPED"] - _DO_HANDSHAKE: Literal["DO_HANDSHAKE"] - _WRAPPED: Literal["WRAPPED"] - _SHUTDOWN: Literal["SHUTDOWN"] + _UNWRAPPED: Final = "UNWRAPPED" + _DO_HANDSHAKE: Final = "DO_HANDSHAKE" + _WRAPPED: Final = "WRAPPED" + _SHUTDOWN: Final = "SHUTDOWN" if sys.version_info < (3, 11): class _SSLPipe: diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index c3cc7b8c9e5a3..ed95583c1847d 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -1,7 +1,8 @@ import ssl import sys from _typeshed import ReadableBuffer, StrPath -from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence, Sized +from collections.abc import Awaitable, Callable, Iterable, Sequence, Sized +from types import ModuleType from typing import Any, Protocol, SupportsIndex from typing_extensions import Self, TypeAlias @@ -130,10 +131,13 @@ class StreamWriter: async def start_tls( self, sslcontext: ssl.SSLContext, *, server_hostname: str | None = None, ssl_handshake_timeout: float | None = None ) -> None: ... - if sys.version_info >= (3, 11): + + if sys.version_info >= (3, 13): + def __del__(self, warnings: ModuleType = ...) -> None: ... + elif sys.version_info >= (3, 11): def __del__(self) -> None: ... -class StreamReader(AsyncIterator[bytes]): +class StreamReader: def __init__(self, limit: int = 65536, loop: events.AbstractEventLoop | None = None) -> None: ... def exception(self) -> Exception: ... def set_exception(self, exc: Exception) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index c16a1919b7c8f..d1ff7d425ba45 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -1,16 +1,20 @@ import concurrent.futures import sys -from collections.abc import Awaitable, Coroutine, Generator, Iterable, Iterator -from types import FrameType -from typing import Any, Literal, Protocol, TextIO, TypeVar, overload +from _asyncio import ( + Task as Task, + _enter_task as _enter_task, + _leave_task as _leave_task, + _register_task as _register_task, + _unregister_task as _unregister_task, +) +from collections.abc import AsyncIterator, Awaitable, Coroutine, Generator, Iterable, Iterator +from typing import Any, Literal, Protocol, TypeVar, overload from typing_extensions import TypeAlias from . import _CoroutineLike from .events import AbstractEventLoop from .futures import Future -if sys.version_info >= (3, 9): - from types import GenericAlias if sys.version_info >= (3, 11): from contextvars import Context @@ -70,14 +74,22 @@ _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") _T6 = TypeVar("_T6") _FT = TypeVar("_FT", bound=Future[Any]) -_FutureLike: TypeAlias = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] +if sys.version_info >= (3, 12): + _FutureLike: TypeAlias = Future[_T] | Awaitable[_T] +else: + _FutureLike: TypeAlias = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] _TaskYieldType: TypeAlias = Future[object] | None FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION ALL_COMPLETED = concurrent.futures.ALL_COMPLETED -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 13): + class _SyncAndAsyncIterator(Iterator[_T_co], AsyncIterator[_T_co], Protocol[_T_co]): ... + + def as_completed(fs: Iterable[_FutureLike[_T]], *, timeout: float | None = None) -> _SyncAndAsyncIterator[Future[_T]]: ... + +elif sys.version_info >= (3, 10): def as_completed(fs: Iterable[_FutureLike[_T]], *, timeout: float | None = None) -> Iterator[Future[_T]]: ... else: @@ -148,13 +160,13 @@ if sys.version_info >= (3, 10): @overload def gather(*coros_or_futures: _FutureLike[_T], return_exceptions: Literal[False] = False) -> Future[list[_T]]: ... # type: ignore[overload-overlap] @overload - def gather(coro_or_future1: _FutureLike[_T1], /, *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[overload-overlap] + def gather(coro_or_future1: _FutureLike[_T1], /, *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], /, *, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], @@ -163,7 +175,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], @@ -173,7 +185,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], @@ -186,7 +198,7 @@ if sys.version_info >= (3, 10): tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException] ]: ... @overload - def gather( # type: ignore[overload-overlap] + def gather( coro_or_future1: _FutureLike[_T1], coro_or_future2: _FutureLike[_T2], coro_or_future3: _FutureLike[_T3], @@ -397,54 +409,6 @@ elif sys.version_info >= (3, 9): else: _TaskCompatibleCoro: TypeAlias = Generator[_TaskYieldType, None, _T_co] | Awaitable[_T_co] -# mypy and pyright complain that a subclass of an invariant class shouldn't be covariant. -# While this is true in general, here it's sort-of okay to have a covariant subclass, -# since the only reason why `asyncio.Future` is invariant is the `set_result()` method, -# and `asyncio.Task.set_result()` always raises. -class Task(Future[_T_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments] - if sys.version_info >= (3, 12): - def __init__( - self, - coro: _TaskCompatibleCoro[_T_co], - *, - loop: AbstractEventLoop = ..., - name: str | None = ..., - context: Context | None = None, - eager_start: bool = False, - ) -> None: ... - elif sys.version_info >= (3, 11): - def __init__( - self, - coro: _TaskCompatibleCoro[_T_co], - *, - loop: AbstractEventLoop = ..., - name: str | None = ..., - context: Context | None = None, - ) -> None: ... - else: - def __init__( - self, coro: _TaskCompatibleCoro[_T_co], *, loop: AbstractEventLoop = ..., name: str | None = ... - ) -> None: ... - - def get_coro(self) -> _TaskCompatibleCoro[_T_co]: ... - def get_name(self) -> str: ... - def set_name(self, value: object, /) -> None: ... - if sys.version_info >= (3, 12): - def get_context(self) -> Context: ... - - def get_stack(self, *, limit: int | None = None) -> list[FrameType]: ... - def print_stack(self, *, limit: int | None = None, file: TextIO | None = None) -> None: ... - if sys.version_info >= (3, 11): - def cancelling(self) -> int: ... - def uncancel(self) -> int: ... - if sys.version_info < (3, 9): - @classmethod - def current_task(cls, loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... - @classmethod - def all_tasks(cls, loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - def all_tasks(loop: AbstractEventLoop | None = None) -> set[Task[Any]]: ... if sys.version_info >= (3, 11): @@ -453,9 +417,10 @@ if sys.version_info >= (3, 11): else: def create_task(coro: _CoroutineLike[_T], *, name: str | None = None) -> Task[_T]: ... -def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... -def _enter_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... -def _leave_task(loop: AbstractEventLoop, task: Task[Any]) -> None: ... +if sys.version_info >= (3, 12): + from _asyncio import current_task as current_task +else: + def current_task(loop: AbstractEventLoop | None = None) -> Task[Any] | None: ... if sys.version_info >= (3, 12): _TaskT_co = TypeVar("_TaskT_co", bound=Task[Any], covariant=True) @@ -492,6 +457,3 @@ if sys.version_info >= (3, 12): name: str | None = None, context: Context | None = None, ) -> Task[_T_co]: ... - -def _register_task(task: Task[Any]) -> None: ... -def _unregister_task(task: Task[Any]) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index e9274b8532909..fb21c5b5fa05f 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -1,63 +1,34 @@ import sys import types +from _typeshed import StrPath from abc import ABCMeta, abstractmethod from collections.abc import Callable +from socket import socket from typing import Literal from typing_extensions import Self, TypeVarTuple, Unpack, deprecated +from .base_events import Server, _ProtocolFactory, _SSLContext from .events import AbstractEventLoop, BaseDefaultEventLoopPolicy from .selector_events import BaseSelectorEventLoop _Ts = TypeVarTuple("_Ts") -# This is also technically not available on Win, -# but other parts of typeshed need this definition. -# So, it is special cased. -if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class AbstractChildWatcher: - @abstractmethod - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - @abstractmethod - def remove_child_handler(self, pid: int) -> bool: ... - @abstractmethod - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - @abstractmethod - def close(self) -> None: ... - @abstractmethod - def __enter__(self) -> Self: ... - @abstractmethod - def __exit__( - self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None - ) -> None: ... - @abstractmethod - def is_active(self) -> bool: ... - -else: - class AbstractChildWatcher: - @abstractmethod - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - @abstractmethod - def remove_child_handler(self, pid: int) -> bool: ... - @abstractmethod - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - @abstractmethod - def close(self) -> None: ... - @abstractmethod - def __enter__(self) -> Self: ... - @abstractmethod - def __exit__( - self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None - ) -> None: ... - @abstractmethod - def is_active(self) -> bool: ... - if sys.platform != "win32": - if sys.version_info >= (3, 9): + if sys.version_info >= (3, 14): + __all__ = ("SelectorEventLoop", "DefaultEventLoopPolicy", "EventLoop") + elif sys.version_info >= (3, 13): + __all__ = ( + "SelectorEventLoop", + "AbstractChildWatcher", + "SafeChildWatcher", + "FastChildWatcher", + "PidfdChildWatcher", + "MultiLoopChildWatcher", + "ThreadedChildWatcher", + "DefaultEventLoopPolicy", + "EventLoop", + ) + elif sys.version_info >= (3, 9): __all__ = ( "SelectorEventLoop", "AbstractChildWatcher", @@ -79,118 +50,202 @@ if sys.platform != "win32": "DefaultEventLoopPolicy", ) - # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. - # See discussion in #7412 - class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): - def close(self) -> None: ... - def is_active(self) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - +# This is also technically not available on Win, +# but other parts of typeshed need this definition. +# So, it is special cased. +if sys.version_info < (3, 14): if sys.version_info >= (3, 12): @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class SafeChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + class AbstractChildWatcher: + @abstractmethod def add_child_handler( self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] ) -> None: ... + @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... - - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class FastChildWatcher(BaseChildWatcher): + @abstractmethod + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod + def close(self) -> None: ... + @abstractmethod def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + @abstractmethod + def __exit__( + self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + @abstractmethod + def is_active(self) -> bool: ... else: - class SafeChildWatcher(BaseChildWatcher): - def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... + class AbstractChildWatcher: + @abstractmethod def add_child_handler( self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] ) -> None: ... + @abstractmethod def remove_child_handler(self, pid: int) -> bool: ... - - class FastChildWatcher(BaseChildWatcher): + @abstractmethod + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @abstractmethod + def close(self) -> None: ... + @abstractmethod def __enter__(self) -> Self: ... - def __exit__(self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + @abstractmethod + def __exit__( + self, typ: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... - - class _UnixSelectorEventLoop(BaseSelectorEventLoop): ... + @abstractmethod + def is_active(self) -> bool: ... - class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy): +if sys.platform != "win32": + if sys.version_info < (3, 14): if sys.version_info >= (3, 12): + # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. + # See discussion in #7412 + class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): + def close(self) -> None: ... + def is_active(self) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def get_child_watcher(self) -> AbstractChildWatcher: ... + class SafeChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + class FastChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + else: - def get_child_watcher(self) -> AbstractChildWatcher: ... - def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + # Doesn't actually have ABCMeta metaclass at runtime, but mypy complains if we don't have it in the stub. + # See discussion in #7412 + class BaseChildWatcher(AbstractChildWatcher, metaclass=ABCMeta): + def close(self) -> None: ... + def is_active(self) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + + class SafeChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + + class FastChildWatcher(BaseChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, a: type[BaseException] | None, b: BaseException | None, c: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + + class _UnixSelectorEventLoop(BaseSelectorEventLoop): + if sys.version_info >= (3, 13): + async def create_unix_server( + self, + protocol_factory: _ProtocolFactory, + path: StrPath | None = None, + *, + sock: socket | None = None, + backlog: int = 100, + ssl: _SSLContext = None, + ssl_handshake_timeout: float | None = None, + ssl_shutdown_timeout: float | None = None, + start_serving: bool = True, + cleanup_socket: bool = True, + ) -> Server: ... + + class _UnixDefaultEventLoopPolicy(BaseDefaultEventLoopPolicy): + if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def get_child_watcher(self) -> AbstractChildWatcher: ... + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... + else: + def get_child_watcher(self) -> AbstractChildWatcher: ... + def set_child_watcher(self, watcher: AbstractChildWatcher | None) -> None: ... SelectorEventLoop = _UnixSelectorEventLoop DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy - if sys.version_info >= (3, 12): - @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") - class MultiLoopChildWatcher(AbstractChildWatcher): - def is_active(self) -> bool: ... - def close(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None - ) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + if sys.version_info >= (3, 13): + EventLoop = SelectorEventLoop - else: - class MultiLoopChildWatcher(AbstractChildWatcher): - def is_active(self) -> bool: ... + if sys.version_info < (3, 14): + if sys.version_info >= (3, 12): + @deprecated("Deprecated as of Python 3.12; will be removed in Python 3.14") + class MultiLoopChildWatcher(AbstractChildWatcher): + def is_active(self) -> bool: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + + else: + class MultiLoopChildWatcher(AbstractChildWatcher): + def is_active(self) -> bool: ... + def close(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + + if sys.version_info < (3, 14): + class ThreadedChildWatcher(AbstractChildWatcher): + def is_active(self) -> Literal[True]: ... def close(self) -> None: ... def __enter__(self) -> Self: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None ) -> None: ... + def __del__(self) -> None: ... def add_child_handler( self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] ) -> None: ... def remove_child_handler(self, pid: int) -> bool: ... def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - class ThreadedChildWatcher(AbstractChildWatcher): - def is_active(self) -> Literal[True]: ... - def close(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None - ) -> None: ... - def __del__(self) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - - if sys.version_info >= (3, 9): - class PidfdChildWatcher(AbstractChildWatcher): - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None - ) -> None: ... - def is_active(self) -> bool: ... - def close(self) -> None: ... - def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... - def add_child_handler( - self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] - ) -> None: ... - def remove_child_handler(self, pid: int) -> bool: ... + if sys.version_info >= (3, 9): + class PidfdChildWatcher(AbstractChildWatcher): + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None + ) -> None: ... + def is_active(self) -> bool: ... + def close(self) -> None: ... + def attach_loop(self, loop: AbstractEventLoop | None) -> None: ... + def add_child_handler( + self, pid: int, callback: Callable[[int, int, Unpack[_Ts]], object], *args: Unpack[_Ts] + ) -> None: ... + def remove_child_handler(self, pid: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index 9c150ee16bebc..e5205ba4dcb07 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -2,24 +2,36 @@ import socket import sys from _typeshed import Incomplete, ReadableBuffer, WriteableBuffer from collections.abc import Callable -from typing import IO, Any, ClassVar, Literal, NoReturn +from typing import IO, Any, ClassVar, Final, NoReturn from . import events, futures, proactor_events, selector_events, streams, windows_utils if sys.platform == "win32": - __all__ = ( - "SelectorEventLoop", - "ProactorEventLoop", - "IocpProactor", - "DefaultEventLoopPolicy", - "WindowsSelectorEventLoopPolicy", - "WindowsProactorEventLoopPolicy", - ) + if sys.version_info >= (3, 13): + # 3.13 added `EventLoop`. + __all__ = ( + "SelectorEventLoop", + "ProactorEventLoop", + "IocpProactor", + "DefaultEventLoopPolicy", + "WindowsSelectorEventLoopPolicy", + "WindowsProactorEventLoopPolicy", + "EventLoop", + ) + else: + __all__ = ( + "SelectorEventLoop", + "ProactorEventLoop", + "IocpProactor", + "DefaultEventLoopPolicy", + "WindowsSelectorEventLoopPolicy", + "WindowsProactorEventLoopPolicy", + ) - NULL: Literal[0] - INFINITE: Literal[0xFFFFFFFF] - ERROR_CONNECTION_REFUSED: Literal[1225] - ERROR_CONNECTION_ABORTED: Literal[1236] + NULL: Final = 0 + INFINITE: Final = 0xFFFFFFFF + ERROR_CONNECTION_REFUSED: Final = 1225 + ERROR_CONNECTION_ABORTED: Final = 1236 CONNECT_PIPE_INIT_DELAY: float CONNECT_PIPE_MAX_DELAY: float @@ -74,8 +86,9 @@ if sys.platform == "win32": class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): _loop_factory: ClassVar[type[SelectorEventLoop]] - def get_child_watcher(self) -> NoReturn: ... - def set_child_watcher(self, watcher: Any) -> NoReturn: ... + if sys.version_info < (3, 14): + def get_child_watcher(self) -> NoReturn: ... + def set_child_watcher(self, watcher: Any) -> NoReturn: ... class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy): _loop_factory: ClassVar[type[ProactorEventLoop]] @@ -83,3 +96,5 @@ if sys.platform == "win32": def set_child_watcher(self, watcher: Any) -> NoReturn: ... DefaultEventLoopPolicy = WindowsSelectorEventLoopPolicy + if sys.version_info >= (3, 13): + EventLoop = ProactorEventLoop diff --git a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi index 6b3589adc3cb9..4fa0145323762 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi @@ -2,13 +2,13 @@ import subprocess import sys from collections.abc import Callable from types import TracebackType -from typing import Any, AnyStr, Literal +from typing import Any, AnyStr, Final from typing_extensions import Self if sys.platform == "win32": __all__ = ("pipe", "Popen", "PIPE", "PipeHandle") - BUFSIZE: Literal[8192] + BUFSIZE: Final = 8192 PIPE = subprocess.PIPE STDOUT = subprocess.STDOUT def pipe(*, duplex: bool = False, overlapped: tuple[bool, bool] = (True, True), bufsize: int = 8192) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi index a72e986728a72..75bfa91cc3798 100644 --- a/mypy/typeshed/stdlib/bdb.pyi +++ b/mypy/typeshed/stdlib/bdb.pyi @@ -1,8 +1,8 @@ import sys -from _typeshed import ExcInfo, TraceFunction +from _typeshed import ExcInfo, TraceFunction, Unused from collections.abc import Callable, Iterable, Mapping from types import CodeType, FrameType, TracebackType -from typing import IO, Any, Literal, SupportsInt, TypeVar +from typing import IO, Any, Final, SupportsInt, TypeVar from typing_extensions import ParamSpec __all__ = ["BdbQuit", "Bdb", "Breakpoint"] @@ -10,7 +10,10 @@ __all__ = ["BdbQuit", "Bdb", "Breakpoint"] _T = TypeVar("_T") _P = ParamSpec("_P") -GENERATOR_AND_COROUTINE_FLAGS: Literal[672] +# A union of code-object flags at runtime. +# The exact values of code-object flags are implementation details, +# so we don't include the value of this constant in the stubs. +GENERATOR_AND_COROUTINE_FLAGS: Final[int] class BdbQuit(Exception): ... @@ -32,6 +35,9 @@ class Bdb: def dispatch_call(self, frame: FrameType, arg: None) -> TraceFunction: ... def dispatch_return(self, frame: FrameType, arg: Any) -> TraceFunction: ... def dispatch_exception(self, frame: FrameType, arg: ExcInfo) -> TraceFunction: ... + if sys.version_info >= (3, 13): + def dispatch_opcode(self, frame: FrameType, arg: Unused) -> Callable[[FrameType, str, Any], TraceFunction]: ... + def is_skipped_module(self, module_name: str) -> bool: ... def stop_here(self, frame: FrameType) -> bool: ... def break_here(self, frame: FrameType) -> bool: ... @@ -42,7 +48,13 @@ class Bdb: def user_return(self, frame: FrameType, return_value: Any) -> None: ... def user_exception(self, frame: FrameType, exc_info: ExcInfo) -> None: ... def set_until(self, frame: FrameType, lineno: int | None = None) -> None: ... + if sys.version_info >= (3, 13): + def user_opcode(self, frame: FrameType) -> None: ... # undocumented + def set_step(self) -> None: ... + if sys.version_info >= (3, 13): + def set_stepinstr(self) -> None: ... # undocumented + def set_next(self, frame: FrameType) -> None: ... def set_return(self, frame: FrameType) -> None: ... def set_trace(self, frame: FrameType | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/binhex.pyi b/mypy/typeshed/stdlib/binhex.pyi index d514be3b9b26a..bdead928468f4 100644 --- a/mypy/typeshed/stdlib/binhex.pyi +++ b/mypy/typeshed/stdlib/binhex.pyi @@ -1,14 +1,14 @@ from _typeshed import SizedBuffer -from typing import IO, Any, Literal +from typing import IO, Any, Final from typing_extensions import TypeAlias __all__ = ["binhex", "hexbin", "Error"] class Error(Exception): ... -REASONABLY_LARGE: Literal[32768] -LINELEN: Literal[64] -RUNCHAR: Literal[b"\x90"] +REASONABLY_LARGE: Final = 32768 +LINELEN: Final = 64 +RUNCHAR: Final = b"\x90" class FInfo: Type: str diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 28b0b11a8e5c9..8eb4a0a55d3c5 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -1,3 +1,4 @@ +# ruff: noqa: PYI036 # This is the module declaring BaseException import _ast import _typeshed import sys @@ -8,6 +9,7 @@ from _typeshed import ( ConvertibleToFloat, ConvertibleToInt, FileDescriptorOrPath, + MaybeNone, OpenBinaryMode, OpenBinaryModeReading, OpenBinaryModeUpdating, @@ -33,7 +35,8 @@ from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from types import CellType, CodeType, TracebackType -# mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} are imported from collections.abc in builtins.pyi +# mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} +# are imported from collections.abc in builtins.pyi from typing import ( # noqa: Y022 IO, Any, @@ -74,6 +77,7 @@ if sys.version_info >= (3, 9): from types import GenericAlias _T = TypeVar("_T") +_I = TypeVar("_I", default=int) _T_co = TypeVar("_T_co", covariant=True) _T_contra = TypeVar("_T_contra", contravariant=True) _R_co = TypeVar("_R_co", covariant=True) @@ -90,6 +94,9 @@ _SupportsAnextT = TypeVar("_SupportsAnextT", bound=SupportsAnext[Any], covariant _AwaitableT = TypeVar("_AwaitableT", bound=Awaitable[Any]) _AwaitableT_co = TypeVar("_AwaitableT_co", bound=Awaitable[Any], covariant=True) _P = ParamSpec("_P") +_StartT = TypeVar("_StartT", covariant=True, default=Any) +_StopT = TypeVar("_StopT", covariant=True, default=Any) +_StepT = TypeVar("_StepT", covariant=True, default=Any) class object: __doc__: str | None @@ -99,7 +106,7 @@ class object: @property def __class__(self) -> type[Self]: ... @__class__.setter - def __class__(self, type: type[object], /) -> None: ... + def __class__(self, type: type[Self], /) -> None: ... def __init__(self) -> None: ... def __new__(cls) -> Self: ... # N.B. `object.__setattr__` and `object.__delattr__` are heavily special-cased by type checkers. @@ -728,8 +735,12 @@ class bytearray(MutableSequence[int]): def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... +_IntegerFormats: TypeAlias = Literal[ + "b", "B", "@b", "@B", "h", "H", "@h", "@H", "i", "I", "@i", "@I", "l", "L", "@l", "@L", "q", "Q", "@q", "@Q", "P", "@P" +] + @final -class memoryview(Sequence[int]): +class memoryview(Sequence[_I]): @property def format(self) -> str: ... @property @@ -759,20 +770,27 @@ class memoryview(Sequence[int]): def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... - def cast(self, format: str, shape: list[int] | tuple[int, ...] = ...) -> memoryview: ... @overload - def __getitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], /) -> int: ... + def cast(self, format: Literal["c", "@c"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[bytes]: ... + @overload + def cast(self, format: Literal["f", "@f", "d", "@d"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[float]: ... + @overload + def cast(self, format: Literal["?"], shape: list[int] | tuple[int, ...] = ...) -> memoryview[bool]: ... @overload - def __getitem__(self, key: slice, /) -> memoryview: ... + def cast(self, format: _IntegerFormats, shape: list[int] | tuple[int, ...] = ...) -> memoryview: ... + @overload + def __getitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], /) -> _I: ... + @overload + def __getitem__(self, key: slice, /) -> memoryview[_I]: ... def __contains__(self, x: object, /) -> bool: ... - def __iter__(self) -> Iterator[int]: ... + def __iter__(self) -> Iterator[_I]: ... def __len__(self) -> int: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... @overload def __setitem__(self, key: slice, value: ReadableBuffer, /) -> None: ... @overload - def __setitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], value: SupportsIndex, /) -> None: ... + def __setitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], value: _I, /) -> None: ... if sys.version_info >= (3, 10): def tobytes(self, order: Literal["C", "F", "A"] | None = "C") -> bytes: ... else: @@ -785,6 +803,11 @@ class memoryview(Sequence[int]): def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... + # These are inherited from the Sequence ABC, but don't actually exist on memoryview. + # See https://github.com/python/cpython/issues/125420 + index: ClassVar[None] # type: ignore[assignment] + count: ClassVar[None] # type: ignore[assignment] + @final class bool(int): def __new__(cls, o: object = ..., /) -> Self: ... @@ -819,19 +842,31 @@ class bool(int): def __invert__(self) -> int: ... @final -class slice: +class slice(Generic[_StartT, _StopT, _StepT]): @property - def start(self) -> Any: ... + def start(self) -> _StartT: ... @property - def step(self) -> Any: ... + def step(self) -> _StepT: ... @property - def stop(self) -> Any: ... + def stop(self) -> _StopT: ... + @overload + def __new__(cls, stop: int | None, /) -> slice[int | MaybeNone, int | MaybeNone, int | MaybeNone]: ... + @overload + def __new__( + cls, start: int | None, stop: int | None, step: int | None = None, / + ) -> slice[int | MaybeNone, int | MaybeNone, int | MaybeNone]: ... + @overload + def __new__(cls, stop: _T2, /) -> slice[Any, _T2, Any]: ... @overload - def __new__(cls, stop: Any, /) -> Self: ... + def __new__(cls, start: _T1, stop: _T2, /) -> slice[_T1, _T2, Any]: ... @overload - def __new__(cls, start: Any, stop: Any, step: Any = ..., /) -> Self: ... + def __new__(cls, start: _T1, stop: _T2, step: _T3, /) -> slice[_T1, _T2, _T3]: ... def __eq__(self, value: object, /) -> bool: ... - __hash__: ClassVar[None] # type: ignore[assignment] + if sys.version_info >= (3, 12): + def __hash__(self) -> int: ... + else: + __hash__: ClassVar[None] # type: ignore[assignment] + def indices(self, len: SupportsIndex, /) -> tuple[int, int, int]: ... class tuple(Sequence[_T_co]): @@ -860,7 +895,9 @@ class tuple(Sequence[_T_co]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -# Doesn't exist at runtime, but deleting this breaks mypy. See #2999 +# Doesn't exist at runtime, but deleting this breaks mypy and pyright. See: +# https://github.com/python/typeshed/issues/7580 +# https://github.com/python/mypy/issues/8240 @final @type_check_only class function: @@ -977,7 +1014,8 @@ class dict(MutableMapping[_KT, _VT]): def keys(self) -> dict_keys[_KT, _VT]: ... def values(self) -> dict_values[_KT, _VT]: ... def items(self) -> dict_items[_KT, _VT]: ... - # Signature of `dict.fromkeys` should be kept identical to `fromkeys` methods of `OrderedDict`/`ChainMap`/`UserDict` in `collections` + # Signature of `dict.fromkeys` should be kept identical to + # `fromkeys` methods of `OrderedDict`/`ChainMap`/`UserDict` in `collections` # TODO: the true signature of `dict.fromkeys` is not expressible in the current type system. # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @@ -1093,7 +1131,7 @@ class frozenset(AbstractSet[_T_co]): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... class enumerate(Iterator[tuple[int, _T]]): - def __new__(cls, iterable: Iterable[_T], start: int = ...) -> Self: ... + def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ... def __iter__(self) -> Self: ... def __next__(self) -> tuple[int, _T]: ... if sys.version_info >= (3, 9): @@ -1148,9 +1186,7 @@ class property: @final class _NotImplementedType(Any): - # A little weird, but typing the __call__ as NotImplemented makes the error message - # for NotImplemented() much better - __call__: NotImplemented # type: ignore[valid-type] # pyright: ignore[reportInvalidTypeForm] + __call__: None NotImplemented: _NotImplementedType @@ -1637,7 +1673,7 @@ _SupportsSumNoDefaultT = TypeVar("_SupportsSumNoDefaultT", bound=_SupportsSumWit # without creating many false-positive errors (see #7578). # Instead, we special-case the most common examples of this: bool and literal integers. @overload -def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... # type: ignore[overload-overlap] +def sum(iterable: Iterable[bool], /, start: int = 0) -> int: ... @overload def sum(iterable: Iterable[_SupportsSumNoDefaultT], /) -> _SupportsSumNoDefaultT | Literal[0]: ... @overload @@ -1645,9 +1681,8 @@ def sum(iterable: Iterable[_AddableT1], /, start: _AddableT2) -> _AddableT1 | _A # The argument to `vars()` has to have a `__dict__` attribute, so the second overload can't be annotated with `object` # (A "SupportsDunderDict" protocol doesn't work) -# Use a type: ignore to make complaints about overlapping overloads go away @overload -def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... # type: ignore[overload-overlap] +def vars(object: type, /) -> types.MappingProxyType[str, Any]: ... @overload def vars(object: Any = ..., /) -> dict[str, Any]: ... @@ -1761,6 +1796,7 @@ class BaseException: __suppress_context__: bool __traceback__: TracebackType | None def __init__(self, *args: object) -> None: ... + def __new__(cls, *args: Any, **kwds: Any) -> Self: ... def __setstate__(self, state: dict[str, Any] | None, /) -> None: ... def with_traceback(self, tb: TracebackType | None, /) -> Self: ... if sys.version_info >= (3, 11): @@ -1780,8 +1816,8 @@ class StopIteration(Exception): value: Any class OSError(Exception): - errno: int - strerror: str + errno: int | None + strerror: str | None # filename, filename2 are actually str | bytes | None filename: Any filename2: Any @@ -1829,14 +1865,33 @@ class StopAsyncIteration(Exception): class SyntaxError(Exception): msg: str + filename: str | None lineno: int | None offset: int | None text: str | None - filename: str | None + # Errors are displayed differently if this attribute exists on the exception. + # The value is always None. + print_file_and_line: None if sys.version_info >= (3, 10): end_lineno: int | None end_offset: int | None + @overload + def __init__(self) -> None: ... + @overload + def __init__(self, msg: object, /) -> None: ... + # Second argument is the tuple (filename, lineno, offset, text) + @overload + def __init__(self, msg: str, info: tuple[str | None, int | None, int | None, str | None], /) -> None: ... + if sys.version_info >= (3, 10): + # end_lineno and end_offset must both be provided if one is. + @overload + def __init__( + self, msg: str, info: tuple[str | None, int | None, int | None, str | None, int | None, int | None], / + ) -> None: ... + # If you provide more than two arguments, it still creates the SyntaxError, but + # the arguments from the info tuple are not parsed. This form is omitted. + class SystemError(Exception): ... class TypeError(Exception): ... class ValueError(Exception): ... @@ -1911,9 +1966,9 @@ if sys.version_info >= (3, 10): class EncodingWarning(Warning): ... if sys.version_info >= (3, 11): - _BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True) + _BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True, default=BaseException) _BaseExceptionT = TypeVar("_BaseExceptionT", bound=BaseException) - _ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True) + _ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True, default=Exception) _ExceptionT = TypeVar("_ExceptionT", bound=Exception) # See `check_exception_group.py` for use-cases and comments. @@ -1977,5 +2032,4 @@ if sys.version_info >= (3, 11): ) -> tuple[ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None]: ... if sys.version_info >= (3, 13): - class IncompleteInputError(SyntaxError): ... class PythonFinalizationError(RuntimeError): ... diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index a7837e1b9ff8a..2f869f9697f4f 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -1,9 +1,10 @@ import _compression import sys +from _bz2 import BZ2Compressor as BZ2Compressor, BZ2Decompressor as BZ2Decompressor from _compression import BaseStream from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Iterable -from typing import IO, Any, Literal, Protocol, SupportsIndex, TextIO, final, overload +from typing import IO, Any, Literal, Protocol, SupportsIndex, TextIO, overload from typing_extensions import Self, TypeAlias __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "decompress"] @@ -125,22 +126,7 @@ class BZ2File(BaseStream, IO[bytes]): def readline(self, size: SupportsIndex = -1) -> bytes: ... # type: ignore[override] def readinto(self, b: WriteableBuffer) -> int: ... def readlines(self, size: SupportsIndex = -1) -> list[bytes]: ... + def peek(self, n: int = 0) -> bytes: ... def seek(self, offset: int, whence: int = 0) -> int: ... def write(self, data: ReadableBuffer) -> int: ... def writelines(self, seq: Iterable[ReadableBuffer]) -> None: ... - -@final -class BZ2Compressor: - def __init__(self, compresslevel: int = ...) -> None: ... - def compress(self, data: ReadableBuffer, /) -> bytes: ... - def flush(self) -> bytes: ... - -@final -class BZ2Decompressor: - def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... - @property - def eof(self) -> bool: ... - @property - def needs_input(self) -> bool: ... - @property - def unused_data(self) -> bytes: ... diff --git a/mypy/typeshed/stdlib/cProfile.pyi b/mypy/typeshed/stdlib/cProfile.pyi index 0cf6e34ec99ed..e921584d43905 100644 --- a/mypy/typeshed/stdlib/cProfile.pyi +++ b/mypy/typeshed/stdlib/cProfile.pyi @@ -1,6 +1,6 @@ import _lsprof from _typeshed import StrOrBytesPath, Unused -from collections.abc import Callable +from collections.abc import Callable, Mapping from types import CodeType from typing import Any, TypeVar from typing_extensions import ParamSpec, Self, TypeAlias @@ -9,7 +9,7 @@ __all__ = ["run", "runctx", "Profile"] def run(statement: str, filename: str | None = None, sort: str | int = -1) -> None: ... def runctx( - statement: str, globals: dict[str, Any], locals: dict[str, Any], filename: str | None = None, sort: str | int = -1 + statement: str, globals: dict[str, Any], locals: Mapping[str, Any], filename: str | None = None, sort: str | int = -1 ) -> None: ... _T = TypeVar("_T") @@ -23,7 +23,7 @@ class Profile(_lsprof.Profiler): def create_stats(self) -> None: ... def snapshot_stats(self) -> None: ... def run(self, cmd: str) -> Self: ... - def runctx(self, cmd: str, globals: dict[str, Any], locals: dict[str, Any]) -> Self: ... + def runctx(self, cmd: str, globals: dict[str, Any], locals: Mapping[str, Any]) -> Self: ... def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... def __enter__(self) -> Self: ... def __exit__(self, *exc_info: Unused) -> None: ... diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index 39312d0b25238..cabf3b881c30f 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -79,9 +79,9 @@ class Calendar: def monthdatescalendar(self, year: int, month: int) -> list[list[datetime.date]]: ... def monthdays2calendar(self, year: int, month: int) -> list[list[tuple[int, int]]]: ... def monthdayscalendar(self, year: int, month: int) -> list[list[int]]: ... - def yeardatescalendar(self, year: int, width: int = 3) -> list[list[int]]: ... - def yeardays2calendar(self, year: int, width: int = 3) -> list[list[tuple[int, int]]]: ... - def yeardayscalendar(self, year: int, width: int = 3) -> list[list[int]]: ... + def yeardatescalendar(self, year: int, width: int = 3) -> list[list[list[list[datetime.date]]]]: ... + def yeardays2calendar(self, year: int, width: int = 3) -> list[list[list[list[tuple[int, int]]]]]: ... + def yeardayscalendar(self, year: int, width: int = 3) -> list[list[list[list[int]]]]: ... def itermonthdays3(self, year: int, month: int) -> Iterable[tuple[int, int, int]]: ... def itermonthdays4(self, year: int, month: int) -> Iterable[tuple[int, int, int, int]]: ... diff --git a/mypy/typeshed/stdlib/cmd.pyi b/mypy/typeshed/stdlib/cmd.pyi index 9499847fb1534..6e84133572bf5 100644 --- a/mypy/typeshed/stdlib/cmd.pyi +++ b/mypy/typeshed/stdlib/cmd.pyi @@ -1,10 +1,11 @@ from collections.abc import Callable -from typing import IO, Any, Literal +from typing import IO, Any, Final +from typing_extensions import LiteralString __all__ = ["Cmd"] -PROMPT: Literal["(Cmd) "] -IDENTCHARS: str # Too big to be `Literal` +PROMPT: Final = "(Cmd) " +IDENTCHARS: Final[LiteralString] # Too big to be `Literal` class Cmd: prompt: str diff --git a/mypy/typeshed/stdlib/code.pyi b/mypy/typeshed/stdlib/code.pyi index 02689238a9a51..54971f3ae93cf 100644 --- a/mypy/typeshed/stdlib/code.pyi +++ b/mypy/typeshed/stdlib/code.pyi @@ -12,7 +12,11 @@ class InteractiveInterpreter: def __init__(self, locals: Mapping[str, Any] | None = None) -> None: ... def runsource(self, source: str, filename: str = "", symbol: str = "single") -> bool: ... def runcode(self, code: CodeType) -> None: ... - def showsyntaxerror(self, filename: str | None = None) -> None: ... + if sys.version_info >= (3, 13): + def showsyntaxerror(self, filename: str | None = None, *, source: str = "") -> None: ... + else: + def showsyntaxerror(self, filename: str | None = None) -> None: ... + def showtraceback(self) -> None: ... def write(self, data: str) -> None: ... diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index 6e53b780c4736..b3c721f1e283b 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -3,7 +3,7 @@ from _codecs import * from _typeshed import ReadableBuffer from abc import abstractmethod from collections.abc import Callable, Generator, Iterable -from typing import Any, BinaryIO, Literal, Protocol, TextIO +from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO from typing_extensions import Self __all__ = [ @@ -53,10 +53,10 @@ __all__ = [ "lookup_error", ] -BOM32_BE: Literal[b"\xfe\xff"] -BOM32_LE: Literal[b"\xff\xfe"] -BOM64_BE: Literal[b"\x00\x00\xfe\xff"] -BOM64_LE: Literal[b"\xff\xfe\x00\x00"] +BOM32_BE: Final = b"\xfe\xff" +BOM32_LE: Final = b"\xff\xfe" +BOM64_BE: Final = b"\x00\x00\xfe\xff" +BOM64_LE: Final = b"\xff\xfe\x00\x00" class _WritableStream(Protocol): def write(self, data: bytes, /) -> object: ... @@ -80,7 +80,7 @@ class _Encoder(Protocol): def __call__(self, input: str, errors: str = ..., /) -> tuple[bytes, int]: ... # signature of Codec().encode class _Decoder(Protocol): - def __call__(self, input: bytes, errors: str = ..., /) -> tuple[str, int]: ... # signature of Codec().decode + def __call__(self, input: ReadableBuffer, errors: str = ..., /) -> tuple[str, int]: ... # signature of Codec().decode class _StreamReader(Protocol): def __call__(self, stream: _ReadableStream, errors: str = ..., /) -> StreamReader: ... @@ -135,23 +135,23 @@ def EncodedFile(file: _Stream, data_encoding: str, file_encoding: str | None = N def iterencode(iterator: Iterable[str], encoding: str, errors: str = "strict") -> Generator[bytes, None, None]: ... def iterdecode(iterator: Iterable[bytes], encoding: str, errors: str = "strict") -> Generator[str, None, None]: ... -BOM: Literal[b"\xff\xfe", b"\xfe\xff"] # depends on `sys.byteorder` -BOM_BE: Literal[b"\xfe\xff"] -BOM_LE: Literal[b"\xff\xfe"] -BOM_UTF8: Literal[b"\xef\xbb\xbf"] -BOM_UTF16: Literal[b"\xff\xfe", b"\xfe\xff"] # depends on `sys.byteorder` -BOM_UTF16_BE: Literal[b"\xfe\xff"] -BOM_UTF16_LE: Literal[b"\xff\xfe"] -BOM_UTF32: Literal[b"\xff\xfe\x00\x00", b"\x00\x00\xfe\xff"] # depends on `sys.byteorder` -BOM_UTF32_BE: Literal[b"\x00\x00\xfe\xff"] -BOM_UTF32_LE: Literal[b"\xff\xfe\x00\x00"] +BOM: Final[Literal[b"\xff\xfe", b"\xfe\xff"]] # depends on `sys.byteorder` +BOM_BE: Final = b"\xfe\xff" +BOM_LE: Final = b"\xff\xfe" +BOM_UTF8: Final = b"\xef\xbb\xbf" +BOM_UTF16: Final[Literal[b"\xff\xfe", b"\xfe\xff"]] # depends on `sys.byteorder` +BOM_UTF16_BE: Final = b"\xfe\xff" +BOM_UTF16_LE: Final = b"\xff\xfe" +BOM_UTF32: Final[Literal[b"\xff\xfe\x00\x00", b"\x00\x00\xfe\xff"]] # depends on `sys.byteorder` +BOM_UTF32_BE: Final = b"\x00\x00\xfe\xff" +BOM_UTF32_LE: Final = b"\xff\xfe\x00\x00" -def strict_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def replace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def ignore_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def xmlcharrefreplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def backslashreplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... -def namereplace_errors(exception: UnicodeError) -> tuple[str | bytes, int]: ... +def strict_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def replace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def ignore_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def xmlcharrefreplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def backslashreplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... +def namereplace_errors(exception: UnicodeError, /) -> tuple[str | bytes, int]: ... class Codec: # These are sort of @abstractmethod but sort of not. @@ -202,6 +202,7 @@ class StreamWriter(Codec): def write(self, object: str) -> None: ... def writelines(self, list: Iterable[str]) -> None: ... def reset(self) -> None: ... + def seek(self, offset: int, whence: int = 0) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ... def __getattr__(self, name: str, getattr: Callable[[Any, str], Any] = ...) -> Any: ... @@ -209,11 +210,14 @@ class StreamWriter(Codec): class StreamReader(Codec): stream: _ReadableStream errors: str + # This is set to str, but some subclasses set to bytes instead. + charbuffertype: ClassVar[type] = ... def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ... def read(self, size: int = -1, chars: int = -1, firstline: bool = False) -> str: ... def readline(self, size: int | None = None, keepends: bool = True) -> str: ... def readlines(self, sizehint: int | None = None, keepends: bool = True) -> list[str]: ... def reset(self) -> None: ... + def seek(self, offset: int, whence: int = 0) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ... def __iter__(self) -> Self: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index 71e3c564dd57f..2d136318813cc 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -17,7 +17,6 @@ if sys.version_info >= (3, 10): Mapping, MutableMapping, MutableSequence, - Reversible, Sequence, ValuesView, ) @@ -331,13 +330,13 @@ class Counter(dict[_T, int], Generic[_T]): # The pure-Python implementations of the "views" classes # These are exposed at runtime in `collections/__init__.py` -class _OrderedDictKeysView(KeysView[_KT_co], Reversible[_KT_co]): +class _OrderedDictKeysView(KeysView[_KT_co]): def __reversed__(self) -> Iterator[_KT_co]: ... -class _OrderedDictItemsView(ItemsView[_KT_co, _VT_co], Reversible[tuple[_KT_co, _VT_co]]): +class _OrderedDictItemsView(ItemsView[_KT_co, _VT_co]): def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... -class _OrderedDictValuesView(ValuesView[_VT_co], Reversible[_VT_co]): +class _OrderedDictValuesView(ValuesView[_VT_co]): def __reversed__(self) -> Iterator[_VT_co]: ... # The C implementations of the "views" classes @@ -345,18 +344,18 @@ class _OrderedDictValuesView(ValuesView[_VT_co], Reversible[_VT_co]): # but they are not exposed anywhere) # pyright doesn't have a specific error code for subclassing error! @final -class _odict_keys(dict_keys[_KT_co, _VT_co], Reversible[_KT_co]): # type: ignore[misc] # pyright: ignore +class _odict_keys(dict_keys[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[_KT_co]: ... @final -class _odict_items(dict_items[_KT_co, _VT_co], Reversible[tuple[_KT_co, _VT_co]]): # type: ignore[misc] # pyright: ignore +class _odict_items(dict_items[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... @final -class _odict_values(dict_values[_KT_co, _VT_co], Reversible[_VT_co], Generic[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore +class _odict_values(dict_values[_KT_co, _VT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __reversed__(self) -> Iterator[_VT_co]: ... -class OrderedDict(dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): +class OrderedDict(dict[_KT, _VT]): def popitem(self, last: bool = True) -> tuple[_KT, _VT]: ... def move_to_end(self, key: _KT, last: bool = True) -> None: ... def copy(self) -> Self: ... @@ -475,7 +474,8 @@ class ChainMap(MutableMapping[_KT, _VT]): def pop(self, key: _KT, default: _T) -> _VT | _T: ... def copy(self) -> Self: ... __copy__ = copy - # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime, so the signature should be kept in line with `dict.fromkeys`. + # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime, + # so the signature should be kept in line with `dict.fromkeys`. @classmethod @overload def fromkeys(cls, iterable: Iterable[_T]) -> ChainMap[_T, Any | None]: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi index 07314ce9d4027..68fd0bc5acb43 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/__init__.pyi @@ -1,3 +1,5 @@ +import sys + from ._base import ( ALL_COMPLETED as ALL_COMPLETED, FIRST_COMPLETED as FIRST_COMPLETED, @@ -14,19 +16,36 @@ from ._base import ( from .process import ProcessPoolExecutor as ProcessPoolExecutor from .thread import ThreadPoolExecutor as ThreadPoolExecutor -__all__ = ( - "FIRST_COMPLETED", - "FIRST_EXCEPTION", - "ALL_COMPLETED", - "CancelledError", - "TimeoutError", - "BrokenExecutor", - "Future", - "Executor", - "wait", - "as_completed", - "ProcessPoolExecutor", - "ThreadPoolExecutor", -) +if sys.version_info >= (3, 13): + __all__ = ( + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "CancelledError", + "TimeoutError", + "InvalidStateError", + "BrokenExecutor", + "Future", + "Executor", + "wait", + "as_completed", + "ProcessPoolExecutor", + "ThreadPoolExecutor", + ) +else: + __all__ = ( + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "CancelledError", + "TimeoutError", + "BrokenExecutor", + "Future", + "Executor", + "wait", + "as_completed", + "ProcessPoolExecutor", + "ThreadPoolExecutor", + ) def __dir__() -> tuple[str, ...]: ... diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index 3d5eccfc048dc..0c019457902b0 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -4,20 +4,20 @@ from _typeshed import Unused from collections.abc import Callable, Collection, Iterable, Iterator from logging import Logger from types import TracebackType -from typing import Any, Generic, Literal, NamedTuple, Protocol, TypeVar +from typing import Any, Final, Generic, NamedTuple, Protocol, TypeVar from typing_extensions import ParamSpec, Self if sys.version_info >= (3, 9): from types import GenericAlias -FIRST_COMPLETED: Literal["FIRST_COMPLETED"] -FIRST_EXCEPTION: Literal["FIRST_EXCEPTION"] -ALL_COMPLETED: Literal["ALL_COMPLETED"] -PENDING: Literal["PENDING"] -RUNNING: Literal["RUNNING"] -CANCELLED: Literal["CANCELLED"] -CANCELLED_AND_NOTIFIED: Literal["CANCELLED_AND_NOTIFIED"] -FINISHED: Literal["FINISHED"] +FIRST_COMPLETED: Final = "FIRST_COMPLETED" +FIRST_EXCEPTION: Final = "FIRST_EXCEPTION" +ALL_COMPLETED: Final = "ALL_COMPLETED" +PENDING: Final = "PENDING" +RUNNING: Final = "RUNNING" +CANCELLED: Final = "CANCELLED" +CANCELLED_AND_NOTIFIED: Final = "CANCELLED_AND_NOTIFIED" +FINISHED: Final = "FINISHED" _FUTURE_STATES: list[str] _STATE_TO_DESCRIPTION_MAP: dict[str, str] LOGGER: Logger diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi index d3706a9c15a6f..a1de3d679b236 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi @@ -19,8 +19,9 @@ _global_shutdown: bool class _ThreadWakeup: _closed: bool - _reader: Connection - _writer: Connection + # Any: Unused send and recv methods + _reader: Connection[Any, Any] + _writer: Connection[Any, Any] def close(self) -> None: ... def wakeup(self) -> None: ... def clear(self) -> None: ... diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index f38bb1de674de..a44dc2e1c0356 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -1,8 +1,8 @@ import sys -from _typeshed import StrOrBytesPath, SupportsWrite +from _typeshed import MaybeNone, StrOrBytesPath, SupportsWrite from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence from re import Pattern -from typing import Any, ClassVar, Literal, TypeVar, overload +from typing import Any, ClassVar, Final, Literal, TypeVar, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 13): @@ -83,8 +83,8 @@ _ConverterCallback: TypeAlias = Callable[[str], Any] _ConvertersMap: TypeAlias = dict[str, _ConverterCallback] _T = TypeVar("_T") -DEFAULTSECT: Literal["DEFAULT"] -MAX_INTERPOLATION_DEPTH: Literal[10] +DEFAULTSECT: Final = "DEFAULT" +MAX_INTERPOLATION_DEPTH: Final = 10 class Interpolation: def before_get(self, parser: _Parser, section: str, option: str, value: str, defaults: _Section) -> str: ... @@ -263,11 +263,11 @@ class RawConfigParser(_Parser): ) -> _T: ... # This is incompatible with MutableMapping so we ignore the type @overload # type: ignore[override] - def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | Any: ... + def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | MaybeNone: ... @overload def get( self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T - ) -> str | _T | Any: ... + ) -> str | _T | MaybeNone: ... @overload def items(self, *, raw: bool = False, vars: _Section | None = None) -> ItemsView[str, SectionProxy]: ... @overload @@ -277,6 +277,8 @@ class RawConfigParser(_Parser): def remove_option(self, section: str, option: str) -> bool: ... def remove_section(self, section: str) -> bool: ... def optionxform(self, optionstr: str) -> str: ... + @property + def converters(self) -> ConverterMapping: ... class ConfigParser(RawConfigParser): # This is incompatible with MutableMapping so we ignore the type @@ -300,28 +302,34 @@ class SectionProxy(MutableMapping[str, str]): def parser(self) -> RawConfigParser: ... @property def name(self) -> str: ... - def get( # type: ignore[override] + # This is incompatible with MutableMapping so we ignore the type + @overload # type: ignore[override] + def get( + self, option: str, *, raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, **kwargs: Any + ) -> str | None: ... + @overload + def get( self, option: str, - fallback: str | None = None, + fallback: _T, *, raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, **kwargs: Any, - ) -> str | Any: ... # can be None in RawConfigParser's sections + ) -> str | _T: ... # These are partially-applied version of the methods with the same names in # RawConfigParser; the stubs should be kept updated together @overload - def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int: ... + def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int | None: ... @overload def getint(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> int | _T: ... @overload - def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float: ... + def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float | None: ... @overload def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> float | _T: ... @overload - def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool: ... + def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool | None: ... @overload def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> bool | _T: ... # SectionProxy can have arbitrary attributes when custom converters are used diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index 29ac7cde561a8..dc5d926775f3c 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -1,7 +1,7 @@ import abc import sys from _typeshed import FileDescriptorOrPath, Unused -from abc import abstractmethod +from abc import ABC, abstractmethod from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Generator, Iterator from types import TracebackType from typing import IO, Any, Generic, Protocol, TypeVar, overload, runtime_checkable @@ -38,16 +38,22 @@ _P = ParamSpec("_P") _ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None] _CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc) +# mypy and pyright object to this being both ABC and Protocol. +# At runtime it inherits from ABC and is not a Protocol, but it is on the +# allowlist for use as a Protocol. @runtime_checkable -class AbstractContextManager(Protocol[_T_co, _ExitT_co]): +class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] def __enter__(self) -> _T_co: ... @abstractmethod def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, / ) -> _ExitT_co: ... +# mypy and pyright object to this being both ABC and Protocol. +# At runtime it inherits from ABC and is not a Protocol, but it is on the +# allowlist for use as a Protocol. @runtime_checkable -class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]): +class AbstractAsyncContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] async def __aenter__(self) -> _T_co: ... @abstractmethod async def __aexit__( @@ -55,11 +61,14 @@ class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]): ) -> _ExitT_co: ... class ContextDecorator: + def _recreate_cm(self) -> Self: ... def __call__(self, func: _F) -> _F: ... -class _GeneratorContextManager(AbstractContextManager[_T_co, bool | None], ContextDecorator): +class _GeneratorContextManagerBase: ... + +class _GeneratorContextManager(_GeneratorContextManagerBase, AbstractContextManager[_T_co, bool | None], ContextDecorator): # __init__ and all instance attributes are actually inherited from _GeneratorContextManagerBase - # _GeneratorContextManagerBase is more trouble than it's worth to include in the stub; see #6676 + # adding them there is more trouble than it's worth to include in the stub; see #6676 def __init__(self, func: Callable[..., Iterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: Generator[_T_co, Any, Any] func: Callable[..., Generator[_T_co, Any, Any]] @@ -80,11 +89,14 @@ if sys.version_info >= (3, 10): _AF = TypeVar("_AF", bound=Callable[..., Awaitable[Any]]) class AsyncContextDecorator: + def _recreate_cm(self) -> Self: ... def __call__(self, func: _AF) -> _AF: ... - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co, bool | None], AsyncContextDecorator): + class _AsyncGeneratorContextManager( + _GeneratorContextManagerBase, AbstractAsyncContextManager[_T_co, bool | None], AsyncContextDecorator + ): # __init__ and these attributes are actually defined in the base class _GeneratorContextManagerBase, - # which is more trouble than it's worth to include in the stub (see #6676) + # adding them there is more trouble than it's worth to include in the stub (see #6676) def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: AsyncGenerator[_T_co, Any] func: Callable[..., AsyncGenerator[_T_co, Any]] @@ -95,7 +107,7 @@ if sys.version_info >= (3, 10): ) -> bool | None: ... else: - class _AsyncGeneratorContextManager(AbstractAsyncContextManager[_T_co, bool | None]): + class _AsyncGeneratorContextManager(_GeneratorContextManagerBase, AbstractAsyncContextManager[_T_co, bool | None]): def __init__(self, func: Callable[..., AsyncIterator[_T_co]], args: tuple[Any, ...], kwds: dict[str, Any]) -> None: ... gen: AsyncGenerator[_T_co, Any] func: Callable[..., AsyncGenerator[_T_co, Any]] @@ -141,13 +153,15 @@ class _RedirectStream(AbstractContextManager[_T_io, None]): class redirect_stdout(_RedirectStream[_T_io]): ... class redirect_stderr(_RedirectStream[_T_io]): ... -# In reality this is a subclass of `AbstractContextManager`; -# see #7961 for why we don't do that in the stub -class ExitStack(Generic[_ExitT_co], metaclass=abc.ABCMeta): +class _BaseExitStack(Generic[_ExitT_co]): def enter_context(self, cm: AbstractContextManager[_T, _ExitT_co]) -> _T: ... def push(self, exit: _CM_EF) -> _CM_EF: ... def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... def pop_all(self) -> Self: ... + +# In reality this is a subclass of `AbstractContextManager`; +# see #7961 for why we don't do that in the stub +class ExitStack(_BaseExitStack[_ExitT_co], metaclass=abc.ABCMeta): def close(self) -> None: ... def __enter__(self) -> Self: ... def __exit__( @@ -161,16 +175,12 @@ _ACM_EF = TypeVar("_ACM_EF", bound=AbstractAsyncContextManager[Any, Any] | _Exit # In reality this is a subclass of `AbstractAsyncContextManager`; # see #7961 for why we don't do that in the stub -class AsyncExitStack(Generic[_ExitT_co], metaclass=abc.ABCMeta): - def enter_context(self, cm: AbstractContextManager[_T, _ExitT_co]) -> _T: ... +class AsyncExitStack(_BaseExitStack[_ExitT_co], metaclass=abc.ABCMeta): async def enter_async_context(self, cm: AbstractAsyncContextManager[_T, _ExitT_co]) -> _T: ... - def push(self, exit: _CM_EF) -> _CM_EF: ... def push_async_exit(self, exit: _ACM_EF) -> _ACM_EF: ... - def callback(self, callback: Callable[_P, _T], /, *args: _P.args, **kwds: _P.kwargs) -> Callable[_P, _T]: ... def push_async_callback( self, callback: Callable[_P, Awaitable[_T]], /, *args: _P.args, **kwds: _P.kwargs ) -> Callable[_P, Awaitable[_T]]: ... - def pop_all(self) -> Self: ... async def aclose(self) -> None: ... async def __aenter__(self) -> Self: ... async def __aexit__( diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index dd5ea0acbe2c0..22dc33006e9d4 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -1,63 +1,3 @@ -import sys -from collections.abc import Callable, Iterator, Mapping -from typing import Any, ClassVar, Generic, TypeVar, final, overload -from typing_extensions import ParamSpec - -if sys.version_info >= (3, 9): - from types import GenericAlias +from _contextvars import Context as Context, ContextVar as ContextVar, Token as Token, copy_context as copy_context __all__ = ("Context", "ContextVar", "Token", "copy_context") - -_T = TypeVar("_T") -_D = TypeVar("_D") -_P = ParamSpec("_P") - -@final -class ContextVar(Generic[_T]): - @overload - def __init__(self, name: str) -> None: ... - @overload - def __init__(self, name: str, *, default: _T) -> None: ... - def __hash__(self) -> int: ... - @property - def name(self) -> str: ... - @overload - def get(self) -> _T: ... - @overload - def get(self, default: _T, /) -> _T: ... - @overload - def get(self, default: _D, /) -> _D | _T: ... - def set(self, value: _T, /) -> Token[_T]: ... - def reset(self, token: Token[_T], /) -> None: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - -@final -class Token(Generic[_T]): - @property - def var(self) -> ContextVar[_T]: ... - @property - def old_value(self) -> Any: ... # returns either _T or MISSING, but that's hard to express - MISSING: ClassVar[object] - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - -def copy_context() -> Context: ... - -# It doesn't make sense to make this generic, because for most Contexts each ContextVar will have -# a different value. -@final -class Context(Mapping[ContextVar[Any], Any]): - def __init__(self) -> None: ... - @overload - def get(self, key: ContextVar[_T], default: None = None, /) -> _T | None: ... - @overload - def get(self, key: ContextVar[_T], default: _T, /) -> _T: ... - @overload - def get(self, key: ContextVar[_T], default: _D, /) -> _T | _D: ... - def run(self, callable: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T: ... - def copy(self) -> Context: ... - def __getitem__(self, key: ContextVar[_T], /) -> _T: ... - def __iter__(self) -> Iterator[ContextVar[Any]]: ... - def __len__(self) -> int: ... - def __eq__(self, value: object, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/copy.pyi b/mypy/typeshed/stdlib/copy.pyi index 8a2dcc508e5d1..2cceec6a22509 100644 --- a/mypy/typeshed/stdlib/copy.pyi +++ b/mypy/typeshed/stdlib/copy.pyi @@ -1,8 +1,15 @@ -from typing import Any, TypeVar +import sys +from typing import Any, Protocol, TypeVar +from typing_extensions import Self __all__ = ["Error", "copy", "deepcopy"] _T = TypeVar("_T") +_SR = TypeVar("_SR", bound=_SupportsReplace) + +class _SupportsReplace(Protocol): + # In reality doesn't support args, but there's no other great way to express this. + def __replace__(self, *args: Any, **kwargs: Any) -> Self: ... # None in CPython but non-None in Jython PyStringMap: Any @@ -11,6 +18,10 @@ PyStringMap: Any def deepcopy(x: _T, memo: dict[int, Any] | None = None, _nil: Any = []) -> _T: ... def copy(x: _T) -> _T: ... +if sys.version_info >= (3, 13): + __all__ += ["replace"] + def replace(obj: _SR, /, **changes: Any) -> _SR: ... + class Error(Exception): ... error = Error diff --git a/mypy/typeshed/stdlib/crypt.pyi b/mypy/typeshed/stdlib/crypt.pyi index 1ad0a384eae7d..bd22b5f8daba2 100644 --- a/mypy/typeshed/stdlib/crypt.pyi +++ b/mypy/typeshed/stdlib/crypt.pyi @@ -1,12 +1,20 @@ import sys +from typing import Final, NamedTuple, type_check_only if sys.platform != "win32": - class _Method: ... - METHOD_CRYPT: _Method - METHOD_MD5: _Method - METHOD_SHA256: _Method - METHOD_SHA512: _Method - METHOD_BLOWFISH: _Method + @type_check_only + class _MethodBase(NamedTuple): + name: str + ident: str | None + salt_chars: int + total_size: int + + class _Method(_MethodBase): ... + METHOD_CRYPT: Final[_Method] + METHOD_MD5: Final[_Method] + METHOD_SHA256: Final[_Method] + METHOD_SHA512: Final[_Method] + METHOD_BLOWFISH: Final[_Method] methods: list[_Method] def mksalt(method: _Method | None = None, *, rounds: int | None = None) -> str: ... def crypt(word: str, salt: str | _Method | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index 24f0db3321653..ef93129d65466 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -1,18 +1,13 @@ import sys - -# actually csv.Dialect is a different class to _csv.Dialect at runtime, but for typing purposes, they're identical from _csv import ( QUOTE_ALL as QUOTE_ALL, QUOTE_MINIMAL as QUOTE_MINIMAL, QUOTE_NONE as QUOTE_NONE, QUOTE_NONNUMERIC as QUOTE_NONNUMERIC, - Dialect as Dialect, Error as Error, __version__ as __version__, _DialectLike, _QuotingType, - _reader, - _writer, field_size_limit as field_size_limit, get_dialect as get_dialect, list_dialects as list_dialects, @@ -24,6 +19,10 @@ from _csv import ( if sys.version_info >= (3, 12): from _csv import QUOTE_NOTNULL as QUOTE_NOTNULL, QUOTE_STRINGS as QUOTE_STRINGS +if sys.version_info >= (3, 10): + from _csv import Reader, Writer +else: + from _csv import _reader as Reader, _writer as Writer from _typeshed import SupportsWrite from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence @@ -61,6 +60,17 @@ if sys.version_info < (3, 13): _T = TypeVar("_T") +class Dialect: + delimiter: str + quotechar: str | None + escapechar: str | None + doublequote: bool + skipinitialspace: bool + lineterminator: str + quoting: _QuotingType + strict: bool + def __init__(self) -> None: ... + class excel(Dialect): ... class excel_tab(excel): ... class unix_dialect(Dialect): ... @@ -69,7 +79,7 @@ class DictReader(Iterator[dict[_T | Any, str | Any]], Generic[_T]): fieldnames: Sequence[_T] | None restkey: _T | None restval: str | Any | None - reader: _reader + reader: Reader dialect: _DialectLike line_num: int @overload @@ -117,7 +127,7 @@ class DictWriter(Generic[_T]): fieldnames: Collection[_T] restval: Any | None extrasaction: Literal["raise", "ignore"] - writer: _writer + writer: Writer def __init__( self, f: SupportsWrite[str], diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index dfd61c8f8ffca..3e0e7c45bf156 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -10,13 +10,11 @@ from _ctypes import ( _CanCastTo as _CanCastTo, _CArgObject as _CArgObject, _CData as _CData, - _CDataMeta as _CDataMeta, + _CDataType as _CDataType, _CField as _CField, _Pointer as _Pointer, _PointerLike as _PointerLike, _SimpleCData as _SimpleCData, - _StructUnionBase as _StructUnionBase, - _StructUnionMeta as _StructUnionMeta, addressof as addressof, alignment as alignment, byref as byref, @@ -28,7 +26,7 @@ from _ctypes import ( ) from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure from typing import Any, ClassVar, Generic, TypeVar -from typing_extensions import TypeAlias +from typing_extensions import Self, TypeAlias, deprecated if sys.platform == "win32": from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error @@ -41,6 +39,7 @@ if sys.version_info >= (3, 9): _T = TypeVar("_T") _DLLT = TypeVar("_DLLT", bound=CDLL) +_CT = TypeVar("_CT", bound=_CData) DEFAULT_MODE: int @@ -48,7 +47,7 @@ class ArgumentError(Exception): ... class CDLL: _func_flags_: ClassVar[int] - _func_restype_: ClassVar[_CData] + _func_restype_: ClassVar[_CDataType] _name: str _handle: int _FuncPtr: type[_FuncPointer] @@ -91,15 +90,21 @@ class _NamedFuncPointer(_FuncPointer): __name__: str def CFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = ..., + use_last_error: bool = ..., ) -> type[_FuncPointer]: ... if sys.platform == "win32": def WINFUNCTYPE( - restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ... + restype: type[_CData | _CDataType] | None, + *argtypes: type[_CData | _CDataType], + use_errno: bool = ..., + use_last_error: bool = ..., ) -> type[_FuncPointer]: ... -def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> type[_FuncPointer]: ... +def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ... # Any type that can be implicitly converted to c_void_p when passed as a C function argument. # (bytes is not included here, see below.) @@ -112,12 +117,17 @@ _CVoidConstPLike: TypeAlias = _CVoidPLike | bytes _CastT = TypeVar("_CastT", bound=_CanCastTo) -def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ... +def cast(obj: _CData | _CDataType | _CArgObject | int, typ: type[_CastT]) -> _CastT: ... def create_string_buffer(init: int | bytes, size: int | None = None) -> Array[c_char]: ... c_buffer = create_string_buffer def create_unicode_buffer(init: int | str, size: int | None = None) -> Array[c_wchar]: ... +@deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15") +def SetPointerType( + pointer: type[_Pointer[Any]], cls: Any # noqa: F811 # Redefinition of unused `pointer` from line 22 +) -> None: ... +def ARRAY(typ: _CT, len: int) -> Array[_CT]: ... # Soft Deprecated, no plans to remove if sys.platform == "win32": def DllCanUnloadNow() -> int: ... @@ -126,12 +136,12 @@ if sys.platform == "win32": def memmove(dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ... def memset(dst: _CVoidPLike, c: int, count: int) -> int: ... -def string_at(address: _CVoidConstPLike, size: int = -1) -> bytes: ... +def string_at(ptr: _CVoidConstPLike, size: int = -1) -> bytes: ... if sys.platform == "win32": def WinError(code: int | None = None, descr: str | None = None) -> OSError: ... -def wstring_at(address: _CVoidConstPLike, size: int = -1) -> str: ... +def wstring_at(ptr: _CVoidConstPLike, size: int = -1) -> str: ... class c_byte(_SimpleCData[int]): ... @@ -140,6 +150,8 @@ class c_char(_SimpleCData[bytes]): class c_char_p(_PointerLike, _SimpleCData[bytes | None]): def __init__(self, value: int | bytes | None = ...) -> None: ... + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... class c_double(_SimpleCData[float]): ... class c_longdouble(_SimpleCData[float]): ... # can be an alias for c_double @@ -155,7 +167,13 @@ class c_uint(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ulong(_SimpleCData[int]): ... class c_ulonglong(_SimpleCData[int]): ... # can be an alias for c_ulong class c_ushort(_SimpleCData[int]): ... -class c_void_p(_PointerLike, _SimpleCData[int | None]): ... + +class c_void_p(_PointerLike, _SimpleCData[int | None]): + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... + +c_voidp = c_void_p # backwards compatibility (to a bug) + class c_wchar(_SimpleCData[str]): ... c_int8 = c_byte @@ -174,6 +192,8 @@ class c_uint64(_SimpleCData[int]): ... class c_wchar_p(_PointerLike, _SimpleCData[str | None]): def __init__(self, value: int | str | None = ...) -> None: ... + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... class c_bool(_SimpleCData[bool]): def __init__(self, value: bool = ...) -> None: ... @@ -185,3 +205,8 @@ if sys.version_info >= (3, 12): c_time_t: type[c_int32 | c_int64] # alias for one or the other at runtime class py_object(_CanCastTo, _SimpleCData[_T]): ... + +if sys.version_info >= (3, 14): + class c_float_complex(_SimpleCData[complex]): ... + class c_double_complex(_SimpleCData[complex]): ... + class c_longdouble_complex(_SimpleCData[complex]): ... diff --git a/mypy/typeshed/stdlib/ctypes/_endian.pyi b/mypy/typeshed/stdlib/ctypes/_endian.pyi index add6365e615f5..144f5ba5dd40f 100644 --- a/mypy/typeshed/stdlib/ctypes/_endian.pyi +++ b/mypy/typeshed/stdlib/ctypes/_endian.pyi @@ -1,12 +1,5 @@ import sys -from _ctypes import RTLD_GLOBAL as RTLD_GLOBAL, RTLD_LOCAL as RTLD_LOCAL, Structure, Union -from ctypes import DEFAULT_MODE as DEFAULT_MODE, cdll as cdll, pydll as pydll, pythonapi as pythonapi - -if sys.version_info >= (3, 12): - from _ctypes import SIZEOF_TIME_T as SIZEOF_TIME_T - -if sys.platform == "win32": - from ctypes import oledll as oledll, windll as windll +from ctypes import Structure, Union # At runtime, the native endianness is an alias for Structure, # while the other is a subclass with a metaclass added in. diff --git a/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi new file mode 100644 index 0000000000000..bda5b5a7f4cc3 --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/__init__.pyi @@ -0,0 +1 @@ +__version__: str diff --git a/mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi b/mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi new file mode 100644 index 0000000000000..c7e94daa21497 --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/dyld.pyi @@ -0,0 +1,8 @@ +from collections.abc import Mapping +from ctypes.macholib.dylib import dylib_info as dylib_info +from ctypes.macholib.framework import framework_info as framework_info + +__all__ = ["dyld_find", "framework_find", "framework_info", "dylib_info"] + +def dyld_find(name: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ... +def framework_find(fn: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/ctypes/macholib/dylib.pyi b/mypy/typeshed/stdlib/ctypes/macholib/dylib.pyi new file mode 100644 index 0000000000000..95945edfd155c --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/dylib.pyi @@ -0,0 +1,14 @@ +from typing import TypedDict, type_check_only + +__all__ = ["dylib_info"] + +# Actual result is produced by re.match.groupdict() +@type_check_only +class _DylibInfo(TypedDict): + location: str + name: str + shortname: str + version: str | None + suffix: str | None + +def dylib_info(filename: str) -> _DylibInfo | None: ... diff --git a/mypy/typeshed/stdlib/ctypes/macholib/framework.pyi b/mypy/typeshed/stdlib/ctypes/macholib/framework.pyi new file mode 100644 index 0000000000000..e92bf3700e840 --- /dev/null +++ b/mypy/typeshed/stdlib/ctypes/macholib/framework.pyi @@ -0,0 +1,14 @@ +from typing import TypedDict, type_check_only + +__all__ = ["framework_info"] + +# Actual result is produced by re.match.groupdict() +@type_check_only +class _FrameworkInfo(TypedDict): + location: str + name: str + shortname: str + version: str | None + suffix: str | None + +def framework_info(filename: str) -> _FrameworkInfo | None: ... diff --git a/mypy/typeshed/stdlib/ctypes/util.pyi b/mypy/typeshed/stdlib/ctypes/util.pyi index c0274f5e539b9..316f7a2b3e2f5 100644 --- a/mypy/typeshed/stdlib/ctypes/util.pyi +++ b/mypy/typeshed/stdlib/ctypes/util.pyi @@ -4,3 +4,5 @@ def find_library(name: str) -> str | None: ... if sys.platform == "win32": def find_msvcrt() -> str | None: ... + +def test() -> None: ... diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi index 8847860f2002b..e938d8f229571 100644 --- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi +++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi @@ -1,7 +1,7 @@ +from _ctypes import _CArgObject, _CField from ctypes import ( Array, Structure, - _CField, _Pointer, _SimpleCData, c_byte, @@ -21,8 +21,8 @@ from ctypes import ( c_wchar, c_wchar_p, ) -from typing import TypeVar -from typing_extensions import TypeAlias +from typing import Any, TypeVar +from typing_extensions import Self, TypeAlias BYTE = c_byte WORD = c_ushort @@ -241,10 +241,16 @@ LPBYTE = PBYTE PBOOLEAN = PBYTE # LP_c_char -class PCHAR(_Pointer[CHAR]): ... +class PCHAR(_Pointer[CHAR]): + # this is inherited from ctypes.c_char_p, kind of. + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... # LP_c_wchar -class PWCHAR(_Pointer[WCHAR]): ... +class PWCHAR(_Pointer[WCHAR]): + # inherited from ctypes.c_wchar_p, kind of + @classmethod + def from_param(cls, value: Any, /) -> Self | _CArgObject: ... # LP_c_void_p class PHANDLE(_Pointer[HANDLE]): ... diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi index 1df184dbaa60f..edc64a00cd39f 100644 --- a/mypy/typeshed/stdlib/curses/__init__.pyi +++ b/mypy/typeshed/stdlib/curses/__init__.pyi @@ -1,7 +1,9 @@ +import sys from _curses import * -from _curses import _CursesWindow as _CursesWindow +from _curses import window as window +from _typeshed import structseq from collections.abc import Callable -from typing import TypeVar +from typing import Final, TypeVar, final, type_check_only from typing_extensions import Concatenate, ParamSpec # NOTE: The _curses module is ordinarily only available on Unix, but the @@ -19,4 +21,25 @@ COLS: int COLORS: int COLOR_PAIRS: int -def wrapper(func: Callable[Concatenate[_CursesWindow, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ... +def wrapper(func: Callable[Concatenate[window, _P], _T], /, *arg: _P.args, **kwds: _P.kwargs) -> _T: ... + +# typeshed used the name _CursesWindow for the underlying C class before +# it was mapped to the name 'window' in 3.8. +# Kept here as a legacy alias in case any third-party code is relying on it. +_CursesWindow = window + +# At runtime this class is unexposed and calls itself curses.ncurses_version. +# That name would conflict with the actual curses.ncurses_version, which is +# an instance of this class. +@final +@type_check_only +class _ncurses_version(structseq[int], tuple[int, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("major", "minor", "patch") + + @property + def major(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def patch(self) -> int: ... diff --git a/mypy/typeshed/stdlib/curses/panel.pyi b/mypy/typeshed/stdlib/curses/panel.pyi index 3d3448bd9584c..861559d38bc5a 100644 --- a/mypy/typeshed/stdlib/curses/panel.pyi +++ b/mypy/typeshed/stdlib/curses/panel.pyi @@ -1,22 +1 @@ -from _curses import _CursesWindow - -version: str - -class _Curses_Panel: # type is (note the space in the class name) - def above(self) -> _Curses_Panel: ... - def below(self) -> _Curses_Panel: ... - def bottom(self) -> None: ... - def hidden(self) -> bool: ... - def hide(self) -> None: ... - def move(self, y: int, x: int) -> None: ... - def replace(self, win: _CursesWindow) -> None: ... - def set_userptr(self, obj: object) -> None: ... - def show(self) -> None: ... - def top(self) -> None: ... - def userptr(self) -> object: ... - def window(self) -> _CursesWindow: ... - -def bottom_panel() -> _Curses_Panel: ... -def new_panel(win: _CursesWindow, /) -> _Curses_Panel: ... -def top_panel() -> _Curses_Panel: ... -def update_panels() -> _Curses_Panel: ... +from _curses_panel import * diff --git a/mypy/typeshed/stdlib/curses/textpad.pyi b/mypy/typeshed/stdlib/curses/textpad.pyi index ce6eed09b289b..48ef67c9d85f0 100644 --- a/mypy/typeshed/stdlib/curses/textpad.pyi +++ b/mypy/typeshed/stdlib/curses/textpad.pyi @@ -1,11 +1,11 @@ -from _curses import _CursesWindow +from _curses import window from collections.abc import Callable -def rectangle(win: _CursesWindow, uly: int, ulx: int, lry: int, lrx: int) -> None: ... +def rectangle(win: window, uly: int, ulx: int, lry: int, lrx: int) -> None: ... class Textbox: stripspaces: bool - def __init__(self, win: _CursesWindow, insert_mode: bool = False) -> None: ... + def __init__(self, win: window, insert_mode: bool = False) -> None: ... def edit(self, validate: Callable[[int], int] | None = None) -> str: ... def do_command(self, ch: str | int) -> None: ... def gather(self) -> str: ... diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index 30489e6f8b3da..3295b1c1f8356 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -5,7 +5,7 @@ from _typeshed import DataclassInstance from builtins import type as Type # alias to avoid name clashes with fields named "type" from collections.abc import Callable, Iterable, Mapping from typing import Any, Generic, Literal, Protocol, TypeVar, overload -from typing_extensions import TypeAlias, TypeIs +from typing_extensions import Never, TypeAlias, TypeIs if sys.version_info >= (3, 9): from types import GenericAlias @@ -108,7 +108,7 @@ class _DefaultFactory(Protocol[_T_co]): class Field(Generic[_T]): name: str - type: Type[_T] + type: Type[_T] | str | Any default: _T | Literal[_MISSING_TYPE.MISSING] default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING] repr: bool @@ -213,6 +213,10 @@ else: ) -> Any: ... def fields(class_or_instance: DataclassInstance | type[DataclassInstance]) -> tuple[Field[Any], ...]: ... + +# HACK: `obj: Never` typing matches if object argument is using `Any` type. +@overload +def is_dataclass(obj: Never) -> TypeIs[DataclassInstance | type[DataclassInstance]]: ... # type: ignore[narrowed-type-not-subtype] # pyright: ignore[reportGeneralTypeIssues] @overload def is_dataclass(obj: type) -> TypeIs[type[DataclassInstance]]: ... @overload @@ -225,18 +229,17 @@ if sys.version_info >= (3, 9): else: class _InitVarMeta(type): # Not used, instead `InitVar.__class_getitem__` is called. - # pyright ignore is needed because pyright (not unreasonably) thinks this - # is an invalid use of InitVar. - def __getitem__(self, params: Any) -> InitVar[Any]: ... # pyright: ignore + # pyright (not unreasonably) thinks this is an invalid use of InitVar. + def __getitem__(self, params: Any) -> InitVar[Any]: ... # pyright: ignore[reportInvalidTypeForm] class InitVar(Generic[_T], metaclass=_InitVarMeta): type: Type[_T] def __init__(self, type: Type[_T]) -> None: ... if sys.version_info >= (3, 9): @overload - def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ... # pyright: ignore + def __class_getitem__(cls, type: Type[_T]) -> InitVar[_T]: ... # pyright: ignore[reportInvalidTypeForm] @overload - def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore + def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore[reportInvalidTypeForm] if sys.version_info >= (3, 12): def make_dataclass( diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 71522a59d4df8..87037ef39be76 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -1,16 +1,16 @@ import sys from abc import abstractmethod from time import struct_time -from typing import ClassVar, Literal, NamedTuple, NoReturn, SupportsIndex, final, overload -from typing_extensions import Self, TypeAlias, deprecated +from typing import ClassVar, Final, NoReturn, SupportsIndex, final, overload, type_check_only +from typing_extensions import CapsuleType, Self, TypeAlias, deprecated if sys.version_info >= (3, 11): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC") elif sys.version_info >= (3, 9): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR") -MINYEAR: Literal[1] -MAXYEAR: Literal[9999] +MINYEAR: Final = 1 +MAXYEAR: Final = 9999 class tzinfo: @abstractmethod @@ -40,10 +40,17 @@ if sys.version_info >= (3, 11): UTC: timezone if sys.version_info >= (3, 9): - class _IsoCalendarDate(NamedTuple): - year: int - week: int - weekday: int + # This class calls itself datetime.IsoCalendarDate. It's neither + # NamedTuple nor structseq. + @final + @type_check_only + class _IsoCalendarDate(tuple[int, int, int]): + @property + def year(self) -> int: ... + @property + def week(self) -> int: ... + @property + def weekday(self) -> int: ... class date: min: ClassVar[date] @@ -265,12 +272,12 @@ class datetime(date): def fromtimestamp(cls, timestamp: float, /, tz: _TzInfo | None = ...) -> Self: ... @classmethod - @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.UTC)") + @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .fromtimestamp(datetime.timezone.utc)") def utcfromtimestamp(cls, t: float, /) -> Self: ... @classmethod def now(cls, tz: _TzInfo | None = None) -> Self: ... @classmethod - @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.UTC)") + @deprecated("Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.timezone.utc)") def utcnow(cls) -> Self: ... @classmethod def combine(cls, date: _Date, time: _Time, tzinfo: _TzInfo | None = ...) -> Self: ... @@ -325,3 +332,5 @@ class datetime(date): def __sub__(self, value: Self, /) -> timedelta: ... @overload def __sub__(self, value: timedelta, /) -> Self: ... + +datetime_CAPI: CapsuleType diff --git a/mypy/typeshed/stdlib/dbm/__init__.pyi b/mypy/typeshed/stdlib/dbm/__init__.pyi index f414763e02a67..7f344060f9ab5 100644 --- a/mypy/typeshed/stdlib/dbm/__init__.pyi +++ b/mypy/typeshed/stdlib/dbm/__init__.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import StrOrBytesPath from collections.abc import Iterator, MutableMapping from types import TracebackType -from typing import Literal +from typing import Literal, type_check_only from typing_extensions import Self, TypeAlias __all__ = ["open", "whichdb", "error"] @@ -89,6 +89,8 @@ class _Database(MutableMapping[_KeyType, bytes]): self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... +# This class is not exposed. It calls itself dbm.error. +@type_check_only class _error(Exception): ... error: tuple[type[_error], type[OSError]] diff --git a/mypy/typeshed/stdlib/dbm/gnu.pyi b/mypy/typeshed/stdlib/dbm/gnu.pyi index e80441cbb25b4..2dac3d12b0ca4 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -1,44 +1 @@ -import sys -from _typeshed import ReadOnlyBuffer, StrOrBytesPath -from types import TracebackType -from typing import TypeVar, overload -from typing_extensions import Self, TypeAlias - -if sys.platform != "win32": - _T = TypeVar("_T") - _KeyType: TypeAlias = str | ReadOnlyBuffer - _ValueType: TypeAlias = str | ReadOnlyBuffer - - open_flags: str - - class error(OSError): ... - # Actual typename gdbm, not exposed by the implementation - class _gdbm: - def firstkey(self) -> bytes | None: ... - def nextkey(self, key: _KeyType) -> bytes | None: ... - def reorganize(self) -> None: ... - def sync(self) -> None: ... - def close(self) -> None: ... - def __getitem__(self, item: _KeyType) -> bytes: ... - def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... - def __delitem__(self, key: _KeyType) -> None: ... - def __contains__(self, key: _KeyType) -> bool: ... - def __len__(self) -> int: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - @overload - def get(self, k: _KeyType) -> bytes | None: ... - @overload - def get(self, k: _KeyType, default: _T) -> bytes | _T: ... - def keys(self) -> list[bytes]: ... - def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... - # Don't exist at runtime - __new__: None # type: ignore[assignment] - __init__: None # type: ignore[assignment] - - if sys.version_info >= (3, 11): - def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... - else: - def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _gdbm: ... +from _gdbm import * diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index 02bf23ec181c5..66c943ab640be 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -1,40 +1 @@ -import sys -from _typeshed import ReadOnlyBuffer, StrOrBytesPath -from types import TracebackType -from typing import TypeVar, overload -from typing_extensions import Self, TypeAlias - -if sys.platform != "win32": - _T = TypeVar("_T") - _KeyType: TypeAlias = str | ReadOnlyBuffer - _ValueType: TypeAlias = str | ReadOnlyBuffer - - class error(OSError): ... - library: str - - # Actual typename dbm, not exposed by the implementation - class _dbm: - def close(self) -> None: ... - def __getitem__(self, item: _KeyType) -> bytes: ... - def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ... - def __delitem__(self, key: _KeyType) -> None: ... - def __len__(self) -> int: ... - def __del__(self) -> None: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - @overload - def get(self, k: _KeyType) -> bytes | None: ... - @overload - def get(self, k: _KeyType, default: _T) -> bytes | _T: ... - def keys(self) -> list[bytes]: ... - def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ... - # Don't exist at runtime - __new__: None # type: ignore[assignment] - __init__: None # type: ignore[assignment] - - if sys.version_info >= (3, 11): - def open(filename: StrOrBytesPath, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... - else: - def open(filename: str, flags: str = "r", mode: int = 0o666, /) -> _dbm: ... +from _dbm import * diff --git a/mypy/typeshed/stdlib/dbm/sqlite3.pyi b/mypy/typeshed/stdlib/dbm/sqlite3.pyi new file mode 100644 index 0000000000000..446a0cf155fa7 --- /dev/null +++ b/mypy/typeshed/stdlib/dbm/sqlite3.pyi @@ -0,0 +1,29 @@ +from _typeshed import ReadableBuffer, StrOrBytesPath, Unused +from collections.abc import Generator, MutableMapping +from typing import Final, Literal +from typing_extensions import LiteralString, Self, TypeAlias + +BUILD_TABLE: Final[LiteralString] +GET_SIZE: Final[LiteralString] +LOOKUP_KEY: Final[LiteralString] +STORE_KV: Final[LiteralString] +DELETE_KEY: Final[LiteralString] +ITER_KEYS: Final[LiteralString] + +_SqliteData: TypeAlias = str | ReadableBuffer | int | float + +class error(OSError): ... + +class _Database(MutableMapping[bytes, bytes]): + def __init__(self, path: StrOrBytesPath, /, *, flag: Literal["r", "w", "c", "n"], mode: int) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: _SqliteData) -> bytes: ... + def __setitem__(self, key: _SqliteData, value: _SqliteData) -> None: ... + def __delitem__(self, key: _SqliteData) -> None: ... + def __iter__(self) -> Generator[bytes]: ... + def close(self) -> None: ... + def keys(self) -> list[bytes]: ... # type: ignore[override] + def __enter__(self) -> Self: ... + def __exit__(self, *args: Unused) -> None: ... + +def open(filename: StrOrBytesPath, /, flag: Literal["r", "w,", "c", "n"] = "r", mode: int = 0o666) -> _Database: ... diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi index 35fc4405f11b5..7f8708a020fdf 100644 --- a/mypy/typeshed/stdlib/decimal.pyi +++ b/mypy/typeshed/stdlib/decimal.pyi @@ -1,2 +1,265 @@ -from _decimal import * -from _decimal import __libmpdec_version__ as __libmpdec_version__, __version__ as __version__ +import numbers +from _decimal import ( + HAVE_CONTEXTVAR as HAVE_CONTEXTVAR, + HAVE_THREADS as HAVE_THREADS, + MAX_EMAX as MAX_EMAX, + MAX_PREC as MAX_PREC, + MIN_EMIN as MIN_EMIN, + MIN_ETINY as MIN_ETINY, + ROUND_05UP as ROUND_05UP, + ROUND_CEILING as ROUND_CEILING, + ROUND_DOWN as ROUND_DOWN, + ROUND_FLOOR as ROUND_FLOOR, + ROUND_HALF_DOWN as ROUND_HALF_DOWN, + ROUND_HALF_EVEN as ROUND_HALF_EVEN, + ROUND_HALF_UP as ROUND_HALF_UP, + ROUND_UP as ROUND_UP, + BasicContext as BasicContext, + DefaultContext as DefaultContext, + ExtendedContext as ExtendedContext, + __libmpdec_version__ as __libmpdec_version__, + __version__ as __version__, + getcontext as getcontext, + localcontext as localcontext, + setcontext as setcontext, +) +from collections.abc import Container, Sequence +from types import TracebackType +from typing import Any, ClassVar, Literal, NamedTuple, final, overload, type_check_only +from typing_extensions import Self, TypeAlias + +_Decimal: TypeAlias = Decimal | int +_DecimalNew: TypeAlias = Decimal | float | str | tuple[int, Sequence[int], int] +_ComparableNum: TypeAlias = Decimal | float | numbers.Rational +_TrapType: TypeAlias = type[DecimalException] + +# At runtime, these classes are implemented in C as part of "_decimal". +# However, they consider themselves to live in "decimal", so we'll put them here. + +# This type isn't exposed at runtime. It calls itself decimal.ContextManager +@final +@type_check_only +class _ContextManager: + def __init__(self, new_context: Context) -> None: ... + def __enter__(self) -> Context: ... + def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... + +class DecimalTuple(NamedTuple): + sign: int + digits: tuple[int, ...] + exponent: int | Literal["n", "N", "F"] + +class DecimalException(ArithmeticError): ... +class Clamped(DecimalException): ... +class InvalidOperation(DecimalException): ... +class ConversionSyntax(InvalidOperation): ... +class DivisionByZero(DecimalException, ZeroDivisionError): ... +class DivisionImpossible(InvalidOperation): ... +class DivisionUndefined(InvalidOperation, ZeroDivisionError): ... +class Inexact(DecimalException): ... +class InvalidContext(InvalidOperation): ... +class Rounded(DecimalException): ... +class Subnormal(DecimalException): ... +class Overflow(Inexact, Rounded): ... +class Underflow(Inexact, Rounded, Subnormal): ... +class FloatOperation(DecimalException, TypeError): ... + +class Decimal: + def __new__(cls, value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... + @classmethod + def from_float(cls, f: float, /) -> Self: ... + def __bool__(self) -> bool: ... + def compare(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def __hash__(self) -> int: ... + def as_tuple(self) -> DecimalTuple: ... + def as_integer_ratio(self) -> tuple[int, int]: ... + def to_eng_string(self, context: Context | None = None) -> str: ... + def __abs__(self) -> Decimal: ... + def __add__(self, value: _Decimal, /) -> Decimal: ... + def __divmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def __eq__(self, value: object, /) -> bool: ... + def __floordiv__(self, value: _Decimal, /) -> Decimal: ... + def __ge__(self, value: _ComparableNum, /) -> bool: ... + def __gt__(self, value: _ComparableNum, /) -> bool: ... + def __le__(self, value: _ComparableNum, /) -> bool: ... + def __lt__(self, value: _ComparableNum, /) -> bool: ... + def __mod__(self, value: _Decimal, /) -> Decimal: ... + def __mul__(self, value: _Decimal, /) -> Decimal: ... + def __neg__(self) -> Decimal: ... + def __pos__(self) -> Decimal: ... + def __pow__(self, value: _Decimal, mod: _Decimal | None = None, /) -> Decimal: ... + def __radd__(self, value: _Decimal, /) -> Decimal: ... + def __rdivmod__(self, value: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def __rfloordiv__(self, value: _Decimal, /) -> Decimal: ... + def __rmod__(self, value: _Decimal, /) -> Decimal: ... + def __rmul__(self, value: _Decimal, /) -> Decimal: ... + def __rsub__(self, value: _Decimal, /) -> Decimal: ... + def __rtruediv__(self, value: _Decimal, /) -> Decimal: ... + def __sub__(self, value: _Decimal, /) -> Decimal: ... + def __truediv__(self, value: _Decimal, /) -> Decimal: ... + def remainder_near(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def __float__(self) -> float: ... + def __int__(self) -> int: ... + def __trunc__(self) -> int: ... + @property + def real(self) -> Decimal: ... + @property + def imag(self) -> Decimal: ... + def conjugate(self) -> Decimal: ... + def __complex__(self) -> complex: ... + @overload + def __round__(self) -> int: ... + @overload + def __round__(self, ndigits: int, /) -> Decimal: ... + def __floor__(self) -> int: ... + def __ceil__(self) -> int: ... + def fma(self, other: _Decimal, third: _Decimal, context: Context | None = None) -> Decimal: ... + def __rpow__(self, value: _Decimal, mod: Context | None = None, /) -> Decimal: ... + def normalize(self, context: Context | None = None) -> Decimal: ... + def quantize(self, exp: _Decimal, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def same_quantum(self, other: _Decimal, context: Context | None = None) -> bool: ... + def to_integral_exact(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def to_integral_value(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def to_integral(self, rounding: str | None = None, context: Context | None = None) -> Decimal: ... + def sqrt(self, context: Context | None = None) -> Decimal: ... + def max(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def min(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def adjusted(self) -> int: ... + def canonical(self) -> Decimal: ... + def compare_signal(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def compare_total(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def compare_total_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def copy_abs(self) -> Decimal: ... + def copy_negate(self) -> Decimal: ... + def copy_sign(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def exp(self, context: Context | None = None) -> Decimal: ... + def is_canonical(self) -> bool: ... + def is_finite(self) -> bool: ... + def is_infinite(self) -> bool: ... + def is_nan(self) -> bool: ... + def is_normal(self, context: Context | None = None) -> bool: ... + def is_qnan(self) -> bool: ... + def is_signed(self) -> bool: ... + def is_snan(self) -> bool: ... + def is_subnormal(self, context: Context | None = None) -> bool: ... + def is_zero(self) -> bool: ... + def ln(self, context: Context | None = None) -> Decimal: ... + def log10(self, context: Context | None = None) -> Decimal: ... + def logb(self, context: Context | None = None) -> Decimal: ... + def logical_and(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def logical_invert(self, context: Context | None = None) -> Decimal: ... + def logical_or(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def logical_xor(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def max_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def min_mag(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def next_minus(self, context: Context | None = None) -> Decimal: ... + def next_plus(self, context: Context | None = None) -> Decimal: ... + def next_toward(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def number_class(self, context: Context | None = None) -> str: ... + def radix(self) -> Decimal: ... + def rotate(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def scaleb(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def shift(self, other: _Decimal, context: Context | None = None) -> Decimal: ... + def __reduce__(self) -> tuple[type[Self], tuple[str]]: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def __format__(self, specifier: str, context: Context | None = ..., /) -> str: ... + +class Context: + # TODO: Context doesn't allow you to delete *any* attributes from instances of the class at runtime, + # even settable attributes like `prec` and `rounding`, + # but that's inexpressable in the stub. + # Type checkers either ignore it or misinterpret it + # if you add a `def __delattr__(self, name: str, /) -> NoReturn` method to the stub + prec: int + rounding: str + Emin: int + Emax: int + capitals: int + clamp: int + traps: dict[_TrapType, bool] + flags: dict[_TrapType, bool] + def __init__( + self, + prec: int | None = ..., + rounding: str | None = ..., + Emin: int | None = ..., + Emax: int | None = ..., + capitals: int | None = ..., + clamp: int | None = ..., + flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., + traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., + _ignored_flags: list[_TrapType] | None = ..., + ) -> None: ... + def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... + def clear_flags(self) -> None: ... + def clear_traps(self) -> None: ... + def copy(self) -> Context: ... + def __copy__(self) -> Context: ... + # see https://github.com/python/cpython/issues/94107 + __hash__: ClassVar[None] # type: ignore[assignment] + def Etiny(self) -> int: ... + def Etop(self) -> int: ... + def create_decimal(self, num: _DecimalNew = "0", /) -> Decimal: ... + def create_decimal_from_float(self, f: float, /) -> Decimal: ... + def abs(self, x: _Decimal, /) -> Decimal: ... + def add(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def canonical(self, x: Decimal, /) -> Decimal: ... + def compare(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_signal(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_total(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def compare_total_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def copy_abs(self, x: _Decimal, /) -> Decimal: ... + def copy_decimal(self, x: _Decimal, /) -> Decimal: ... + def copy_negate(self, x: _Decimal, /) -> Decimal: ... + def copy_sign(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divide(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divide_int(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def divmod(self, x: _Decimal, y: _Decimal, /) -> tuple[Decimal, Decimal]: ... + def exp(self, x: _Decimal, /) -> Decimal: ... + def fma(self, x: _Decimal, y: _Decimal, z: _Decimal, /) -> Decimal: ... + def is_canonical(self, x: _Decimal, /) -> bool: ... + def is_finite(self, x: _Decimal, /) -> bool: ... + def is_infinite(self, x: _Decimal, /) -> bool: ... + def is_nan(self, x: _Decimal, /) -> bool: ... + def is_normal(self, x: _Decimal, /) -> bool: ... + def is_qnan(self, x: _Decimal, /) -> bool: ... + def is_signed(self, x: _Decimal, /) -> bool: ... + def is_snan(self, x: _Decimal, /) -> bool: ... + def is_subnormal(self, x: _Decimal, /) -> bool: ... + def is_zero(self, x: _Decimal, /) -> bool: ... + def ln(self, x: _Decimal, /) -> Decimal: ... + def log10(self, x: _Decimal, /) -> Decimal: ... + def logb(self, x: _Decimal, /) -> Decimal: ... + def logical_and(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def logical_invert(self, x: _Decimal, /) -> Decimal: ... + def logical_or(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def logical_xor(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def max(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def max_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def min(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def min_mag(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def minus(self, x: _Decimal, /) -> Decimal: ... + def multiply(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def next_minus(self, x: _Decimal, /) -> Decimal: ... + def next_plus(self, x: _Decimal, /) -> Decimal: ... + def next_toward(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def normalize(self, x: _Decimal, /) -> Decimal: ... + def number_class(self, x: _Decimal, /) -> str: ... + def plus(self, x: _Decimal, /) -> Decimal: ... + def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = None) -> Decimal: ... + def quantize(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def radix(self) -> Decimal: ... + def remainder(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def remainder_near(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def rotate(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def same_quantum(self, x: _Decimal, y: _Decimal, /) -> bool: ... + def scaleb(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def shift(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def sqrt(self, x: _Decimal, /) -> Decimal: ... + def subtract(self, x: _Decimal, y: _Decimal, /) -> Decimal: ... + def to_eng_string(self, x: _Decimal, /) -> str: ... + def to_sci_string(self, x: _Decimal, /) -> str: ... + def to_integral_exact(self, x: _Decimal, /) -> Decimal: ... + def to_integral_value(self, x: _Decimal, /) -> Decimal: ... + def to_integral(self, x: _Decimal, /) -> Decimal: ... diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi index 47c63cc8b3d3d..cb69eac89c920 100644 --- a/mypy/typeshed/stdlib/dis.pyi +++ b/mypy/typeshed/stdlib/dis.pyi @@ -31,6 +31,9 @@ __all__ = [ "EXTENDED_ARG", "stack_effect", ] +if sys.version_info >= (3, 13): + __all__ += ["hasjump"] + if sys.version_info >= (3, 12): __all__ += ["hasarg", "hasexc"] else: @@ -86,12 +89,41 @@ else: is_jump_target: bool class Instruction(_Instruction): - def _disassemble(self, lineno_width: int = 3, mark_as_current: bool = False, offset_width: int = 4) -> str: ... + if sys.version_info < (3, 13): + def _disassemble(self, lineno_width: int = 3, mark_as_current: bool = False, offset_width: int = 4) -> str: ... + if sys.version_info >= (3, 13): + @property + def oparg(self) -> int: ... + @property + def baseopcode(self) -> int: ... + @property + def baseopname(self) -> str: ... + @property + def cache_offset(self) -> int: ... + @property + def end_offset(self) -> int: ... + @property + def jump_target(self) -> int: ... + @property + def is_jump_target(self) -> bool: ... class Bytecode: codeobj: types.CodeType first_line: int - if sys.version_info >= (3, 11): + if sys.version_info >= (3, 13): + show_offsets: bool + # 3.13 added `show_offsets` + def __init__( + self, + x: _HaveCodeType | str, + *, + first_line: int | None = None, + current_offset: int | None = None, + show_caches: bool = False, + adaptive: bool = False, + show_offsets: bool = False, + ) -> None: ... + elif sys.version_info >= (3, 11): def __init__( self, x: _HaveCodeType | str, @@ -101,12 +133,15 @@ class Bytecode: show_caches: bool = False, adaptive: bool = False, ) -> None: ... - @classmethod - def from_traceback(cls, tb: types.TracebackType, *, show_caches: bool = False, adaptive: bool = False) -> Self: ... else: def __init__( self, x: _HaveCodeType | str, *, first_line: int | None = None, current_offset: int | None = None ) -> None: ... + + if sys.version_info >= (3, 11): + @classmethod + def from_traceback(cls, tb: types.TracebackType, *, show_caches: bool = False, adaptive: bool = False) -> Self: ... + else: @classmethod def from_traceback(cls, tb: types.TracebackType) -> Self: ... @@ -121,7 +156,8 @@ def findlinestarts(code: _HaveCodeType) -> Iterator[tuple[int, int]]: ... def pretty_flags(flags: int) -> str: ... def code_info(x: _HaveCodeType | str) -> str: ... -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 13): + # 3.13 added `show_offsets` def dis( x: _HaveCodeType | str | bytes | bytearray | None = None, *, @@ -129,20 +165,43 @@ if sys.version_info >= (3, 11): depth: int | None = None, show_caches: bool = False, adaptive: bool = False, + show_offsets: bool = False, + ) -> None: ... + def disassemble( + co: _HaveCodeType, + lasti: int = -1, + *, + file: IO[str] | None = None, + show_caches: bool = False, + adaptive: bool = False, + show_offsets: bool = False, + ) -> None: ... + def distb( + tb: types.TracebackType | None = None, + *, + file: IO[str] | None = None, + show_caches: bool = False, + adaptive: bool = False, + show_offsets: bool = False, ) -> None: ... + # 3.13 made `show_cache` `None` by default + def get_instructions( + x: _HaveCodeType, *, first_line: int | None = None, show_caches: bool | None = None, adaptive: bool = False + ) -> Iterator[Instruction]: ... -else: +elif sys.version_info >= (3, 11): + # 3.11 added `show_caches` and `adaptive` def dis( - x: _HaveCodeType | str | bytes | bytearray | None = None, *, file: IO[str] | None = None, depth: int | None = None + x: _HaveCodeType | str | bytes | bytearray | None = None, + *, + file: IO[str] | None = None, + depth: int | None = None, + show_caches: bool = False, + adaptive: bool = False, ) -> None: ... - -if sys.version_info >= (3, 11): def disassemble( co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False ) -> None: ... - def disco( - co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False - ) -> None: ... def distb( tb: types.TracebackType | None = None, *, file: IO[str] | None = None, show_caches: bool = False, adaptive: bool = False ) -> None: ... @@ -151,9 +210,13 @@ if sys.version_info >= (3, 11): ) -> Iterator[Instruction]: ... else: + def dis( + x: _HaveCodeType | str | bytes | bytearray | None = None, *, file: IO[str] | None = None, depth: int | None = None + ) -> None: ... def disassemble(co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None) -> None: ... - def disco(co: _HaveCodeType, lasti: int = -1, *, file: IO[str] | None = None) -> None: ... def distb(tb: types.TracebackType | None = None, *, file: IO[str] | None = None) -> None: ... def get_instructions(x: _HaveCodeType, *, first_line: int | None = None) -> Iterator[Instruction]: ... def show_code(co: _HaveCodeType, *, file: IO[str] | None = None) -> None: ... + +disco = disassemble diff --git a/mypy/typeshed/stdlib/distutils/_msvccompiler.pyi b/mypy/typeshed/stdlib/distutils/_msvccompiler.pyi new file mode 100644 index 0000000000000..bba9373b72dbc --- /dev/null +++ b/mypy/typeshed/stdlib/distutils/_msvccompiler.pyi @@ -0,0 +1,13 @@ +from _typeshed import Incomplete +from distutils.ccompiler import CCompiler +from typing import ClassVar, Final + +PLAT_SPEC_TO_RUNTIME: Final[dict[str, str]] +PLAT_TO_VCVARS: Final[dict[str, str]] + +class MSVCCompiler(CCompiler): + compiler_type: ClassVar[str] + executables: ClassVar[dict[Incomplete, Incomplete]] + res_extension: ClassVar[str] + initialized: bool + def initialize(self, plat_name: str | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/ccompiler.pyi b/mypy/typeshed/stdlib/distutils/ccompiler.pyi index cd6efee0a2103..5bff209807eef 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -1,10 +1,11 @@ -from _typeshed import BytesPath, StrPath -from collections.abc import Callable, Iterable +from _typeshed import BytesPath, StrPath, Unused +from collections.abc import Callable, Iterable, Sequence from distutils.file_util import _BytesPathT, _StrPathT -from typing import Any, Literal, overload -from typing_extensions import TypeAlias +from typing import Literal, overload +from typing_extensions import TypeAlias, TypeVarTuple, Unpack _Macro: TypeAlias = tuple[str] | tuple[str, str | None] +_Ts = TypeVarTuple("_Ts") def gen_lib_options( compiler: CCompiler, library_dirs: list[str], runtime_library_dirs: list[str], libraries: list[str] @@ -62,7 +63,7 @@ class CCompiler: def set_executables(self, **args: str) -> None: ... def compile( self, - sources: list[str], + sources: Sequence[StrPath], output_dir: str | None = None, macros: list[_Macro] | None = None, include_dirs: list[str] | None = None, @@ -161,8 +162,10 @@ class CCompiler: def shared_object_filename(self, basename: str, strip_dir: Literal[0, False] = 0, output_dir: StrPath = "") -> str: ... @overload def shared_object_filename(self, basename: StrPath, strip_dir: Literal[1, True], output_dir: StrPath = "") -> str: ... - def execute(self, func: Callable[..., object], args: tuple[Any, ...], msg: str | None = None, level: int = 1) -> None: ... - def spawn(self, cmd: list[str]) -> None: ... + def execute( + self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1 + ) -> None: ... + def spawn(self, cmd: Iterable[str]) -> None: ... def mkpath(self, name: str, mode: int = 0o777) -> None: ... @overload def move_file(self, src: StrPath, dst: _StrPathT) -> _StrPathT | str: ... diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi index defea50e78dc2..dcb423a49b090 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -1,11 +1,36 @@ from _typeshed import BytesPath, Incomplete, StrOrBytesPath, StrPath, Unused from abc import abstractmethod from collections.abc import Callable, Iterable +from distutils.command.bdist import bdist +from distutils.command.bdist_dumb import bdist_dumb +from distutils.command.bdist_rpm import bdist_rpm +from distutils.command.build import build +from distutils.command.build_clib import build_clib +from distutils.command.build_ext import build_ext +from distutils.command.build_py import build_py +from distutils.command.build_scripts import build_scripts +from distutils.command.check import check +from distutils.command.clean import clean +from distutils.command.config import config +from distutils.command.install import install +from distutils.command.install_data import install_data +from distutils.command.install_egg_info import install_egg_info +from distutils.command.install_headers import install_headers +from distutils.command.install_lib import install_lib +from distutils.command.install_scripts import install_scripts +from distutils.command.register import register +from distutils.command.sdist import sdist +from distutils.command.upload import upload from distutils.dist import Distribution from distutils.file_util import _BytesPathT, _StrPathT -from typing import Any, ClassVar, Literal, overload +from typing import Any, ClassVar, Literal, TypeVar, overload +from typing_extensions import TypeVarTuple, Unpack + +_CommandT = TypeVar("_CommandT", bound=Command) +_Ts = TypeVarTuple("_Ts") class Command: + dry_run: Literal[0, 1] # Exposed from __getattr_. Same as Distribution.dry_run distribution: Distribution # Any to work around variance issues sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] @@ -19,17 +44,122 @@ class Command: def announce(self, msg: str, level: int = 1) -> None: ... def debug_print(self, msg: str) -> None: ... def ensure_string(self, option: str, default: str | None = None) -> None: ... - def ensure_string_list(self, option: str | list[str]) -> None: ... + def ensure_string_list(self, option: str) -> None: ... def ensure_filename(self, option: str) -> None: ... def ensure_dirname(self, option: str) -> None: ... def get_command_name(self) -> str: ... def set_undefined_options(self, src_cmd: str, *option_pairs: tuple[str, str]) -> None: ... + # NOTE: This list comes directly from the distutils/command folder. Minus bdist_msi and bdist_wininst. + @overload + def get_finalized_command(self, command: Literal["bdist"], create: bool | Literal[0, 1] = 1) -> bdist: ... + @overload + def get_finalized_command(self, command: Literal["bdist_dumb"], create: bool | Literal[0, 1] = 1) -> bdist_dumb: ... + @overload + def get_finalized_command(self, command: Literal["bdist_rpm"], create: bool | Literal[0, 1] = 1) -> bdist_rpm: ... + @overload + def get_finalized_command(self, command: Literal["build"], create: bool | Literal[0, 1] = 1) -> build: ... + @overload + def get_finalized_command(self, command: Literal["build_clib"], create: bool | Literal[0, 1] = 1) -> build_clib: ... + @overload + def get_finalized_command(self, command: Literal["build_ext"], create: bool | Literal[0, 1] = 1) -> build_ext: ... + @overload + def get_finalized_command(self, command: Literal["build_py"], create: bool | Literal[0, 1] = 1) -> build_py: ... + @overload + def get_finalized_command(self, command: Literal["build_scripts"], create: bool | Literal[0, 1] = 1) -> build_scripts: ... + @overload + def get_finalized_command(self, command: Literal["check"], create: bool | Literal[0, 1] = 1) -> check: ... + @overload + def get_finalized_command(self, command: Literal["clean"], create: bool | Literal[0, 1] = 1) -> clean: ... + @overload + def get_finalized_command(self, command: Literal["config"], create: bool | Literal[0, 1] = 1) -> config: ... + @overload + def get_finalized_command(self, command: Literal["install"], create: bool | Literal[0, 1] = 1) -> install: ... + @overload + def get_finalized_command(self, command: Literal["install_data"], create: bool | Literal[0, 1] = 1) -> install_data: ... + @overload + def get_finalized_command( + self, command: Literal["install_egg_info"], create: bool | Literal[0, 1] = 1 + ) -> install_egg_info: ... + @overload + def get_finalized_command(self, command: Literal["install_headers"], create: bool | Literal[0, 1] = 1) -> install_headers: ... + @overload + def get_finalized_command(self, command: Literal["install_lib"], create: bool | Literal[0, 1] = 1) -> install_lib: ... + @overload + def get_finalized_command(self, command: Literal["install_scripts"], create: bool | Literal[0, 1] = 1) -> install_scripts: ... + @overload + def get_finalized_command(self, command: Literal["register"], create: bool | Literal[0, 1] = 1) -> register: ... + @overload + def get_finalized_command(self, command: Literal["sdist"], create: bool | Literal[0, 1] = 1) -> sdist: ... + @overload + def get_finalized_command(self, command: Literal["upload"], create: bool | Literal[0, 1] = 1) -> upload: ... + @overload def get_finalized_command(self, command: str, create: bool | Literal[0, 1] = 1) -> Command: ... - def reinitialize_command(self, command: Command | str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ... + @overload + def reinitialize_command(self, command: Literal["bdist"], reinit_subcommands: bool | Literal[0, 1] = 0) -> bdist: ... + @overload + def reinitialize_command( + self, command: Literal["bdist_dumb"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> bdist_dumb: ... + @overload + def reinitialize_command(self, command: Literal["bdist_rpm"], reinit_subcommands: bool | Literal[0, 1] = 0) -> bdist_rpm: ... + @overload + def reinitialize_command(self, command: Literal["build"], reinit_subcommands: bool | Literal[0, 1] = 0) -> build: ... + @overload + def reinitialize_command( + self, command: Literal["build_clib"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> build_clib: ... + @overload + def reinitialize_command(self, command: Literal["build_ext"], reinit_subcommands: bool | Literal[0, 1] = 0) -> build_ext: ... + @overload + def reinitialize_command(self, command: Literal["build_py"], reinit_subcommands: bool | Literal[0, 1] = 0) -> build_py: ... + @overload + def reinitialize_command( + self, command: Literal["build_scripts"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> build_scripts: ... + @overload + def reinitialize_command(self, command: Literal["check"], reinit_subcommands: bool | Literal[0, 1] = 0) -> check: ... + @overload + def reinitialize_command(self, command: Literal["clean"], reinit_subcommands: bool | Literal[0, 1] = 0) -> clean: ... + @overload + def reinitialize_command(self, command: Literal["config"], reinit_subcommands: bool | Literal[0, 1] = 0) -> config: ... + @overload + def reinitialize_command(self, command: Literal["install"], reinit_subcommands: bool | Literal[0, 1] = 0) -> install: ... + @overload + def reinitialize_command( + self, command: Literal["install_data"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_data: ... + @overload + def reinitialize_command( + self, command: Literal["install_egg_info"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_egg_info: ... + @overload + def reinitialize_command( + self, command: Literal["install_headers"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_headers: ... + @overload + def reinitialize_command( + self, command: Literal["install_lib"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_lib: ... + @overload + def reinitialize_command( + self, command: Literal["install_scripts"], reinit_subcommands: bool | Literal[0, 1] = 0 + ) -> install_scripts: ... + @overload + def reinitialize_command(self, command: Literal["register"], reinit_subcommands: bool | Literal[0, 1] = 0) -> register: ... + @overload + def reinitialize_command(self, command: Literal["sdist"], reinit_subcommands: bool | Literal[0, 1] = 0) -> sdist: ... + @overload + def reinitialize_command(self, command: Literal["upload"], reinit_subcommands: bool | Literal[0, 1] = 0) -> upload: ... + @overload + def reinitialize_command(self, command: str, reinit_subcommands: bool | Literal[0, 1] = 0) -> Command: ... + @overload + def reinitialize_command(self, command: _CommandT, reinit_subcommands: bool | Literal[0, 1] = 0) -> _CommandT: ... def run_command(self, command: str) -> None: ... def get_sub_commands(self) -> list[str]: ... def warn(self, msg: str) -> None: ... - def execute(self, func: Callable[..., object], args: Iterable[Any], msg: str | None = None, level: int = 1) -> None: ... + def execute( + self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1 + ) -> None: ... def mkpath(self, name: str, mode: int = 0o777) -> None: ... @overload def copy_file( @@ -89,8 +219,8 @@ class Command: self, infiles: str | list[str] | tuple[str, ...], outfile: StrOrBytesPath, - func: Callable[..., object], - args: list[Any], + func: Callable[[Unpack[_Ts]], Unused], + args: tuple[Unpack[_Ts]], exec_msg: str | None = None, skip_msg: str | None = None, level: Unused = 1, diff --git a/mypy/typeshed/stdlib/distutils/command/__init__.pyi b/mypy/typeshed/stdlib/distutils/command/__init__.pyi index e69de29bb2d1d..4d7372858af34 100644 --- a/mypy/typeshed/stdlib/distutils/command/__init__.pyi +++ b/mypy/typeshed/stdlib/distutils/command/__init__.pyi @@ -0,0 +1,48 @@ +import sys + +from . import ( + bdist, + bdist_dumb, + bdist_rpm, + build, + build_clib, + build_ext, + build_py, + build_scripts, + check, + clean, + install, + install_data, + install_headers, + install_lib, + install_scripts, + register, + sdist, + upload, +) + +__all__ = [ + "build", + "build_py", + "build_ext", + "build_clib", + "build_scripts", + "clean", + "install", + "install_lib", + "install_headers", + "install_scripts", + "install_data", + "sdist", + "register", + "bdist", + "bdist_dumb", + "bdist_rpm", + "check", + "upload", +] + +if sys.version_info < (3, 10): + from . import bdist_wininst + + __all__ += ["bdist_wininst"] diff --git a/mypy/typeshed/stdlib/distutils/command/bdist.pyi b/mypy/typeshed/stdlib/distutils/command/bdist.pyi index e1f141d3a40fa..6f996207077e0 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist.pyi @@ -1,4 +1,6 @@ -from typing import Any +from _typeshed import Incomplete, Unused +from collections.abc import Callable +from typing import ClassVar from ..cmd import Command @@ -6,20 +8,20 @@ def show_formats() -> None: ... class bdist(Command): description: str - user_options: Any - boolean_options: Any - help_options: Any - no_format_option: Any - default_format: Any - format_commands: Any - format_command: Any - bdist_base: Any - plat_name: Any - formats: Any - dist_dir: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] + no_format_option: ClassVar[tuple[str, ...]] + default_format: ClassVar[dict[str, str]] + format_commands: ClassVar[list[str]] + format_command: ClassVar[dict[str, tuple[str, str]]] + bdist_base: Incomplete + plat_name: Incomplete + formats: Incomplete + dist_dir: Incomplete skip_build: int - group: Any - owner: Any + group: Incomplete + owner: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi index 74cca4d13cd0b..297a0c39ed430 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_dumb.pyi @@ -1,21 +1,22 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class bdist_dumb(Command): description: str - user_options: Any - boolean_options: Any - default_format: Any - bdist_dir: Any - plat_name: Any - format: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + default_format: ClassVar[dict[str, str]] + bdist_dir: Incomplete + plat_name: Incomplete + format: Incomplete keep_temp: int - dist_dir: Any - skip_build: Any + dist_dir: Incomplete + skip_build: Incomplete relative: int - owner: Any - group: Any + owner: Incomplete + group: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi index d1eb374ff52bd..baeee7d3eccb9 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_msi.pyi @@ -1,42 +1,43 @@ import sys -from typing import Any, Literal +from _typeshed import Incomplete +from typing import ClassVar, Literal from ..cmd import Command if sys.platform == "win32": - from msilib import Dialog + from msilib import Control, Dialog class PyDialog(Dialog): def __init__(self, *args, **kw) -> None: ... def title(self, title) -> None: ... - def back(self, title, next, name: str = "Back", active: bool | Literal[0, 1] = 1): ... - def cancel(self, title, next, name: str = "Cancel", active: bool | Literal[0, 1] = 1): ... - def next(self, title, next, name: str = "Next", active: bool | Literal[0, 1] = 1): ... - def xbutton(self, name, title, next, xpos): ... + def back(self, title, next, name: str = "Back", active: bool | Literal[0, 1] = 1) -> Control: ... + def cancel(self, title, next, name: str = "Cancel", active: bool | Literal[0, 1] = 1) -> Control: ... + def next(self, title, next, name: str = "Next", active: bool | Literal[0, 1] = 1) -> Control: ... + def xbutton(self, name, title, next, xpos) -> Control: ... class bdist_msi(Command): description: str - user_options: Any - boolean_options: Any - all_versions: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + all_versions: Incomplete other_version: str if sys.version_info >= (3, 9): def __init__(self, *args, **kw) -> None: ... - bdist_dir: Any - plat_name: Any + bdist_dir: Incomplete + plat_name: Incomplete keep_temp: int no_target_compile: int no_target_optimize: int - target_version: Any - dist_dir: Any - skip_build: Any - install_script: Any - pre_install_script: Any - versions: Any + target_version: Incomplete + dist_dir: Incomplete + skip_build: Incomplete + install_script: Incomplete + pre_install_script: Incomplete + versions: Incomplete def initialize_options(self) -> None: ... - install_script_key: Any + install_script_key: Incomplete def finalize_options(self) -> None: ... - db: Any + db: Incomplete def run(self) -> None: ... def add_files(self) -> None: ... def add_find_python(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi index 76691310b5999..83b4161094c51 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_rpm.pyi @@ -1,50 +1,51 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class bdist_rpm(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any - bdist_base: Any - rpm_base: Any - dist_dir: Any - python: Any - fix_python: Any - spec_only: Any - binary_only: Any - source_only: Any - use_bzip2: Any - distribution_name: Any - group: Any - release: Any - serial: Any - vendor: Any - packager: Any - doc_files: Any - changelog: Any - icon: Any - prep_script: Any - build_script: Any - install_script: Any - clean_script: Any - verify_script: Any - pre_install: Any - post_install: Any - pre_uninstall: Any - post_uninstall: Any - prep: Any - provides: Any - requires: Any - conflicts: Any - build_requires: Any - obsoletes: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] + bdist_base: Incomplete + rpm_base: Incomplete + dist_dir: Incomplete + python: Incomplete + fix_python: Incomplete + spec_only: Incomplete + binary_only: Incomplete + source_only: Incomplete + use_bzip2: Incomplete + distribution_name: Incomplete + group: Incomplete + release: Incomplete + serial: Incomplete + vendor: Incomplete + packager: Incomplete + doc_files: Incomplete + changelog: Incomplete + icon: Incomplete + prep_script: Incomplete + build_script: Incomplete + install_script: Incomplete + clean_script: Incomplete + verify_script: Incomplete + pre_install: Incomplete + post_install: Incomplete + pre_uninstall: Incomplete + post_uninstall: Incomplete + prep: Incomplete + provides: Incomplete + requires: Incomplete + conflicts: Incomplete + build_requires: Incomplete + obsoletes: Incomplete keep_temp: int use_rpm_opt_flags: int rpm3_mode: int no_autoreq: int - force_arch: Any + force_arch: Incomplete quiet: int def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi b/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi index 8491d31262007..cf333bc5400dd 100644 --- a/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi +++ b/mypy/typeshed/stdlib/distutils/command/bdist_wininst.pyi @@ -1,10 +1,10 @@ from _typeshed import StrOrBytesPath from distutils.cmd import Command -from typing import Any, ClassVar +from typing import ClassVar class bdist_wininst(Command): description: ClassVar[str] - user_options: ClassVar[list[tuple[Any, ...]]] + user_options: ClassVar[list[tuple[str, str | None, str]]] boolean_options: ClassVar[list[str]] def initialize_options(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build.pyi b/mypy/typeshed/stdlib/distutils/command/build.pyi index 31fc036d4f97e..3ec0c9614d62a 100644 --- a/mypy/typeshed/stdlib/distutils/command/build.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build.pyi @@ -1,3 +1,4 @@ +from _typeshed import Incomplete, Unused from collections.abc import Callable from typing import Any, ClassVar @@ -7,21 +8,21 @@ def show_compilers() -> None: ... class build(Command): description: str - user_options: Any - boolean_options: Any - help_options: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] build_base: str - build_purelib: Any - build_platlib: Any - build_lib: Any - build_temp: Any - build_scripts: Any - compiler: Any - plat_name: Any - debug: Any + build_purelib: Incomplete + build_platlib: Incomplete + build_lib: Incomplete + build_temp: Incomplete + build_scripts: Incomplete + compiler: Incomplete + plat_name: Incomplete + debug: Incomplete force: int - executable: Any - parallel: Any + executable: Incomplete + parallel: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_clib.pyi b/mypy/typeshed/stdlib/distutils/command/build_clib.pyi index 32ab182b30d04..69cfbe7120d8e 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_clib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_clib.pyi @@ -1,4 +1,6 @@ -from typing import Any +from _typeshed import Incomplete, Unused +from collections.abc import Callable +from typing import ClassVar from ..cmd import Command @@ -6,18 +8,18 @@ def show_compilers() -> None: ... class build_clib(Command): description: str - user_options: Any - boolean_options: Any - help_options: Any - build_clib: Any - build_temp: Any - libraries: Any - include_dirs: Any - define: Any - undef: Any - debug: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] + build_clib: Incomplete + build_temp: Incomplete + libraries: Incomplete + include_dirs: Incomplete + define: Incomplete + undef: Incomplete + debug: Incomplete force: int - compiler: Any + compiler: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_ext.pyi b/mypy/typeshed/stdlib/distutils/command/build_ext.pyi index 5eb541fb91019..c5a9b5d508f0d 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_ext.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_ext.pyi @@ -1,38 +1,40 @@ -from typing import Any +from _typeshed import Incomplete, Unused +from collections.abc import Callable +from typing import ClassVar from ..cmd import Command -extension_name_re: Any +extension_name_re: Incomplete def show_compilers() -> None: ... class build_ext(Command): description: str - sep_by: Any - user_options: Any - boolean_options: Any - help_options: Any - extensions: Any - build_lib: Any - plat_name: Any - build_temp: Any + sep_by: Incomplete + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] + extensions: Incomplete + build_lib: Incomplete + plat_name: Incomplete + build_temp: Incomplete inplace: int - package: Any - include_dirs: Any - define: Any - undef: Any - libraries: Any - library_dirs: Any - rpath: Any - link_objects: Any - debug: Any - force: Any - compiler: Any - swig: Any - swig_cpp: Any - swig_opts: Any - user: Any - parallel: Any + package: Incomplete + include_dirs: Incomplete + define: Incomplete + undef: Incomplete + libraries: Incomplete + library_dirs: Incomplete + rpath: Incomplete + link_objects: Incomplete + debug: Incomplete + force: Incomplete + compiler: Incomplete + swig: Incomplete + swig_cpp: Incomplete + swig_opts: Incomplete + user: Incomplete + parallel: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_py.pyi b/mypy/typeshed/stdlib/distutils/command/build_py.pyi index 4c607c6dabe90..23ed230bb2d8c 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_py.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_py.pyi @@ -1,24 +1,25 @@ -from typing import Any, Literal +from _typeshed import Incomplete +from typing import ClassVar, Literal from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 class build_py(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any - build_lib: Any - py_modules: Any - package: Any - package_data: Any - package_dir: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] + build_lib: Incomplete + py_modules: Incomplete + package: Incomplete + package_data: Incomplete + package_dir: Incomplete compile: int optimize: int - force: Any + force: Incomplete def initialize_options(self) -> None: ... - packages: Any - data_files: Any + packages: Incomplete + data_files: Incomplete def finalize_options(self) -> None: ... def run(self) -> None: ... def get_data_files(self): ... @@ -32,13 +33,13 @@ class build_py(Command): def find_all_modules(self): ... def get_source_files(self): ... def get_module_outfile(self, build_dir, package, module): ... - def get_outputs(self, include_bytecode: bool | Literal[0, 1] = 1): ... + def get_outputs(self, include_bytecode: bool | Literal[0, 1] = 1) -> list[str]: ... def build_module(self, module, module_file, package): ... def build_modules(self) -> None: ... def build_packages(self) -> None: ... def byte_compile(self, files) -> None: ... class build_py_2to3(build_py, Mixin2to3): - updated_files: Any + updated_files: Incomplete def run(self) -> None: ... def build_module(self, module, module_file, package): ... diff --git a/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi b/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi index 42135eceafefd..8372919bbd530 100644 --- a/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi +++ b/mypy/typeshed/stdlib/distutils/command/build_scripts.pyi @@ -1,19 +1,20 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command from ..util import Mixin2to3 as Mixin2to3 -first_line_re: Any +first_line_re: Incomplete class build_scripts(Command): description: str - user_options: Any - boolean_options: Any - build_dir: Any - scripts: Any - force: Any - executable: Any - outfiles: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] + build_dir: Incomplete + scripts: Incomplete + force: Incomplete + executable: Incomplete + outfiles: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def get_source_files(self): ... diff --git a/mypy/typeshed/stdlib/distutils/command/check.pyi b/mypy/typeshed/stdlib/distutils/command/check.pyi index da041d82587de..2c807fd2c4396 100644 --- a/mypy/typeshed/stdlib/distutils/command/check.pyi +++ b/mypy/typeshed/stdlib/distutils/command/check.pyi @@ -1,4 +1,5 @@ -from typing import Any, Literal +from _typeshed import Incomplete +from typing import Any, ClassVar, Final, Literal from typing_extensions import TypeAlias from ..cmd import Command @@ -9,25 +10,25 @@ _Reporter: TypeAlias = Any # really docutils.utils.Reporter # Depends on a third-party stub. Since distutils is deprecated anyway, # it's easier to just suppress the "any subclassing" error. class SilentReporter(_Reporter): - messages: Any + messages: Incomplete def __init__( self, source, report_level, halt_level, - stream: Any | None = ..., + stream: Incomplete | None = ..., debug: bool | Literal[0, 1] = 0, encoding: str = ..., error_handler: str = ..., ) -> None: ... def system_message(self, level, message, *children, **kwargs): ... -HAS_DOCUTILS: bool +HAS_DOCUTILS: Final[bool] class check(Command): description: str - user_options: Any - boolean_options: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] restructuredtext: int metadata: int strict: int diff --git a/mypy/typeshed/stdlib/distutils/command/clean.pyi b/mypy/typeshed/stdlib/distutils/command/clean.pyi index 99560aa8a716c..0f3768d6dcf4d 100644 --- a/mypy/typeshed/stdlib/distutils/command/clean.pyi +++ b/mypy/typeshed/stdlib/distutils/command/clean.pyi @@ -1,17 +1,18 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class clean(Command): description: str - user_options: Any - boolean_options: Any - build_base: Any - build_lib: Any - build_temp: Any - build_scripts: Any - bdist_base: Any - all: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + build_base: Incomplete + build_lib: Incomplete + build_temp: Incomplete + build_scripts: Incomplete + bdist_base: Incomplete + all: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/config.pyi b/mypy/typeshed/stdlib/distutils/command/config.pyi index 391f5a8620383..562ff3a5271f8 100644 --- a/mypy/typeshed/stdlib/distutils/command/config.pyi +++ b/mypy/typeshed/stdlib/distutils/command/config.pyi @@ -1,17 +1,17 @@ -from _typeshed import StrOrBytesPath +from _typeshed import Incomplete, StrOrBytesPath from collections.abc import Sequence from re import Pattern -from typing import Any, Literal +from typing import ClassVar, Final, Literal from ..ccompiler import CCompiler from ..cmd import Command -LANG_EXT: dict[str, str] +LANG_EXT: Final[dict[str, str]] class config(Command): description: str # Tuple is full name, short name, description - user_options: Sequence[tuple[str, str | None, str]] + user_options: ClassVar[list[tuple[str, str | None, str]]] compiler: str | CCompiler cc: str | None include_dirs: Sequence[str] | None @@ -81,4 +81,4 @@ class config(Command): self, header: str, include_dirs: Sequence[str] | None = None, library_dirs: Sequence[str] | None = None, lang: str = "c" ) -> bool: ... -def dump_file(filename: StrOrBytesPath, head: Any | None = None) -> None: ... +def dump_file(filename: StrOrBytesPath, head: Incomplete | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install.pyi b/mypy/typeshed/stdlib/distutils/command/install.pyi index 8b2295d7a3c7e..1714e01a2c284 100644 --- a/mypy/typeshed/stdlib/distutils/command/install.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install.pyi @@ -1,45 +1,51 @@ +import sys +from _typeshed import Incomplete from collections.abc import Callable -from typing import Any, ClassVar +from typing import Any, ClassVar, Final, Literal from ..cmd import Command -HAS_USER_SITE: bool -SCHEME_KEYS: tuple[str, ...] -INSTALL_SCHEMES: dict[str, dict[Any, Any]] +HAS_USER_SITE: Final[bool] + +SCHEME_KEYS: Final[tuple[Literal["purelib"], Literal["platlib"], Literal["headers"], Literal["scripts"], Literal["data"]]] +INSTALL_SCHEMES: Final[dict[str, dict[str, str]]] + +if sys.version_info < (3, 10): + WINDOWS_SCHEME: Final[dict[str, str]] class install(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] prefix: str | None - exec_prefix: Any + exec_prefix: Incomplete home: str | None user: bool - install_base: Any - install_platbase: Any + install_base: Incomplete + install_platbase: Incomplete root: str | None - install_purelib: Any - install_platlib: Any - install_headers: Any + install_purelib: Incomplete + install_platlib: Incomplete + install_headers: Incomplete install_lib: str | None - install_scripts: Any - install_data: Any - install_userbase: Any - install_usersite: Any - compile: Any - optimize: Any - extra_path: Any + install_scripts: Incomplete + install_data: Incomplete + install_userbase: Incomplete + install_usersite: Incomplete + compile: Incomplete + optimize: Incomplete + extra_path: Incomplete install_path_file: int force: int skip_build: int warn_dir: int - build_base: Any - build_lib: Any - record: Any + build_base: Incomplete + build_lib: Incomplete + record: Incomplete def initialize_options(self) -> None: ... - config_vars: Any - install_libbase: Any + config_vars: Incomplete + install_libbase: Incomplete def finalize_options(self) -> None: ... def dump_dirs(self, msg) -> None: ... def finalize_unix(self) -> None: ... @@ -48,8 +54,8 @@ class install(Command): def expand_basedirs(self) -> None: ... def expand_dirs(self) -> None: ... def convert_paths(self, *names) -> None: ... - path_file: Any - extra_dirs: Any + path_file: Incomplete + extra_dirs: Incomplete def handle_extra_path(self) -> None: ... def change_roots(self, *names) -> None: ... def create_home_path(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_data.pyi b/mypy/typeshed/stdlib/distutils/command/install_data.pyi index 6cc9b528ac9da..609de62b04b52 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_data.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_data.pyi @@ -1,16 +1,17 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class install_data(Command): description: str - user_options: Any - boolean_options: Any - install_dir: Any - outfiles: Any - root: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + install_dir: Incomplete + outfiles: Incomplete + root: Incomplete force: int - data_files: Any + data_files: Incomplete warn_dir: int def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi b/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi index 776eafc1de09c..75bb906ce5824 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_egg_info.pyi @@ -1,14 +1,15 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class install_egg_info(Command): description: ClassVar[str] - user_options: ClassVar[list[tuple[str, str | None, str]]] - install_dir: Any + user_options: ClassVar[list[tuple[str, str, str]]] + install_dir: Incomplete def initialize_options(self) -> None: ... - target: Any - outputs: Any + target: Incomplete + outputs: Incomplete def finalize_options(self) -> None: ... def run(self) -> None: ... def get_outputs(self) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_headers.pyi b/mypy/typeshed/stdlib/distutils/command/install_headers.pyi index 795bd1cf8356b..3caad8a07dca4 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_headers.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_headers.pyi @@ -1,14 +1,15 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class install_headers(Command): description: str - user_options: Any - boolean_options: Any - install_dir: Any + user_options: ClassVar[list[tuple[str, str, str]]] + boolean_options: ClassVar[list[str]] + install_dir: Incomplete force: int - outfiles: Any + outfiles: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi index a6a5e4e73f4c0..a537e254904aa 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_lib.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_lib.pyi @@ -1,20 +1,21 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar, Final from ..cmd import Command -PYTHON_SOURCE_EXTENSION: str +PYTHON_SOURCE_EXTENSION: Final = ".py" class install_lib(Command): description: str - user_options: Any - boolean_options: Any - negative_opt: Any - install_dir: Any - build_dir: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + negative_opt: ClassVar[dict[str, str]] + install_dir: Incomplete + build_dir: Incomplete force: int - compile: Any - optimize: Any - skip_build: Any + compile: Incomplete + optimize: Incomplete + skip_build: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... def run(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi b/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi index 92728a16a7478..658594f32e43c 100644 --- a/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi +++ b/mypy/typeshed/stdlib/distutils/command/install_scripts.pyi @@ -1,18 +1,19 @@ -from typing import Any +from _typeshed import Incomplete +from typing import ClassVar from ..cmd import Command class install_scripts(Command): description: str - user_options: Any - boolean_options: Any - install_dir: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + install_dir: Incomplete force: int - build_dir: Any - skip_build: Any + build_dir: Incomplete + skip_build: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... - outfiles: Any + outfiles: Incomplete def run(self) -> None: ... def get_inputs(self): ... def get_outputs(self): ... diff --git a/mypy/typeshed/stdlib/distutils/command/register.pyi b/mypy/typeshed/stdlib/distutils/command/register.pyi index a5e251d2d01e8..cf98e178a9ba1 100644 --- a/mypy/typeshed/stdlib/distutils/command/register.pyi +++ b/mypy/typeshed/stdlib/distutils/command/register.pyi @@ -1,3 +1,4 @@ +from _typeshed import Incomplete from collections.abc import Callable from typing import Any, ClassVar @@ -17,4 +18,4 @@ class register(PyPIRCCommand): def verify_metadata(self) -> None: ... def send_metadata(self) -> None: ... def build_post_data(self, action): ... - def post_to_server(self, data, auth: Any | None = None): ... + def post_to_server(self, data, auth: Incomplete | None = None): ... diff --git a/mypy/typeshed/stdlib/distutils/command/sdist.pyi b/mypy/typeshed/stdlib/distutils/command/sdist.pyi index db303f77a4634..48a140714dda7 100644 --- a/mypy/typeshed/stdlib/distutils/command/sdist.pyi +++ b/mypy/typeshed/stdlib/distutils/command/sdist.pyi @@ -1,3 +1,4 @@ +from _typeshed import Incomplete, Unused from collections.abc import Callable from typing import Any, ClassVar @@ -8,29 +9,29 @@ def show_formats() -> None: ... class sdist(Command): description: str def checking_metadata(self): ... - user_options: Any - boolean_options: Any - help_options: Any - negative_opt: Any + user_options: ClassVar[list[tuple[str, str | None, str]]] + boolean_options: ClassVar[list[str]] + help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], Unused]]]] + negative_opt: ClassVar[dict[str, str]] # Any to work around variance issues sub_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] - READMES: Any - template: Any - manifest: Any + READMES: ClassVar[tuple[str, ...]] + template: Incomplete + manifest: Incomplete use_defaults: int prune: int manifest_only: int force_manifest: int - formats: Any + formats: Incomplete keep_temp: int - dist_dir: Any - archive_files: Any + dist_dir: Incomplete + archive_files: Incomplete metadata_check: int - owner: Any - group: Any + owner: Incomplete + group: Incomplete def initialize_options(self) -> None: ... def finalize_options(self) -> None: ... - filelist: Any + filelist: Incomplete def run(self) -> None: ... def check_metadata(self) -> None: ... def get_file_list(self) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/command/upload.pyi b/mypy/typeshed/stdlib/distutils/command/upload.pyi index e6b77825c5f5b..afcfbaf48677e 100644 --- a/mypy/typeshed/stdlib/distutils/command/upload.pyi +++ b/mypy/typeshed/stdlib/distutils/command/upload.pyi @@ -1,4 +1,5 @@ -from typing import Any, ClassVar +from _typeshed import Incomplete +from typing import ClassVar from ..config import PyPIRCCommand @@ -8,10 +9,10 @@ class upload(PyPIRCCommand): password: str show_response: int sign: bool - identity: Any + identity: Incomplete def initialize_options(self) -> None: ... - repository: Any - realm: Any + repository: Incomplete + realm: Incomplete def finalize_options(self) -> None: ... def run(self) -> None: ... def upload_file(self, command: str, pyversion: str, filename: str) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/core.pyi b/mypy/typeshed/stdlib/distutils/core.pyi index f3c434df0b1a0..174f249913514 100644 --- a/mypy/typeshed/stdlib/distutils/core.pyi +++ b/mypy/typeshed/stdlib/distutils/core.pyi @@ -1,11 +1,11 @@ -from _typeshed import StrOrBytesPath +from _typeshed import Incomplete, StrOrBytesPath from collections.abc import Mapping from distutils.cmd import Command as Command from distutils.dist import Distribution as Distribution from distutils.extension import Extension as Extension -from typing import Any, Literal +from typing import Any, Final, Literal -USAGE: str +USAGE: Final[str] def gen_usage(script_name: StrOrBytesPath) -> str: ... @@ -32,7 +32,7 @@ def setup( distclass: type[Distribution] = ..., script_name: str = ..., script_args: list[str] = ..., - options: Mapping[str, Any] = ..., + options: Mapping[str, Incomplete] = ..., license: str = ..., keywords: list[str] | str = ..., platforms: list[str] | str = ..., @@ -43,7 +43,7 @@ def setup( provides: list[str] = ..., requires: list[str] = ..., command_packages: list[str] = ..., - command_options: Mapping[str, Mapping[str, tuple[Any, Any]]] = ..., + command_options: Mapping[str, Mapping[str, tuple[Incomplete, Incomplete]]] = ..., package_data: Mapping[str, list[str]] = ..., include_package_data: bool | Literal[0, 1] = ..., libraries: list[str] = ..., @@ -52,6 +52,7 @@ def setup( include_dirs: list[str] = ..., password: str = ..., fullname: str = ..., + # Custom Distributions could accept more params **attrs: Any, ) -> Distribution: ... def run_setup(script_name: str, script_args: list[str] | None = None, stop_after: str = "run") -> Distribution: ... diff --git a/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi b/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi index 5f2e623eeff65..80924d63e4714 100644 --- a/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/cygwinccompiler.pyi @@ -1,20 +1,20 @@ from distutils.unixccompiler import UnixCCompiler from distutils.version import LooseVersion from re import Pattern -from typing import Literal +from typing import Final, Literal def get_msvcr() -> list[str] | None: ... class CygwinCCompiler(UnixCCompiler): ... class Mingw32CCompiler(CygwinCCompiler): ... -CONFIG_H_OK: str -CONFIG_H_NOTOK: str -CONFIG_H_UNCERTAIN: str +CONFIG_H_OK: Final = "ok" +CONFIG_H_NOTOK: Final = "not ok" +CONFIG_H_UNCERTAIN: Final = "uncertain" def check_config_h() -> tuple[Literal["ok", "not ok", "uncertain"], str]: ... -RE_VERSION: Pattern[bytes] +RE_VERSION: Final[Pattern[bytes]] def get_versions() -> tuple[LooseVersion | None, ...]: ... def is_cygwingcc() -> bool: ... diff --git a/mypy/typeshed/stdlib/distutils/debug.pyi b/mypy/typeshed/stdlib/distutils/debug.pyi index 11f28a8bc8ae4..30095883b0644 100644 --- a/mypy/typeshed/stdlib/distutils/debug.pyi +++ b/mypy/typeshed/stdlib/distutils/debug.pyi @@ -1 +1,3 @@ -DEBUG: bool | None +from typing import Final + +DEBUG: Final[str | None] diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index 4094df9033250..75fc7dbb388d6 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -1,8 +1,28 @@ from _typeshed import Incomplete, StrOrBytesPath, StrPath, SupportsWrite -from collections.abc import Iterable, Mapping +from collections.abc import Iterable, MutableMapping from distutils.cmd import Command +from distutils.command.bdist import bdist +from distutils.command.bdist_dumb import bdist_dumb +from distutils.command.bdist_rpm import bdist_rpm +from distutils.command.build import build +from distutils.command.build_clib import build_clib +from distutils.command.build_ext import build_ext +from distutils.command.build_py import build_py +from distutils.command.build_scripts import build_scripts +from distutils.command.check import check +from distutils.command.clean import clean +from distutils.command.config import config +from distutils.command.install import install +from distutils.command.install_data import install_data +from distutils.command.install_egg_info import install_egg_info +from distutils.command.install_headers import install_headers +from distutils.command.install_lib import install_lib +from distutils.command.install_scripts import install_scripts +from distutils.command.register import register +from distutils.command.sdist import sdist +from distutils.command.upload import upload from re import Pattern -from typing import IO, Any, ClassVar, Literal, TypeVar, overload +from typing import IO, ClassVar, Literal, TypeVar, overload from typing_extensions import TypeAlias command_re: Pattern[str] @@ -60,21 +80,17 @@ class DistributionMetadata: class Distribution: cmdclass: dict[str, type[Command]] metadata: DistributionMetadata - def __init__(self, attrs: Mapping[str, Any] | None = None) -> None: ... + def __init__(self, attrs: MutableMapping[str, Incomplete] | None = None) -> None: ... def get_option_dict(self, command: str) -> dict[str, tuple[str, str]]: ... def parse_config_files(self, filenames: Iterable[str] | None = None) -> None: ... - @overload - def get_command_obj(self, command: str, create: Literal[1, True] = 1) -> Command: ... - @overload - def get_command_obj(self, command: str, create: Literal[0, False]) -> Command | None: ... global_options: ClassVar[_OptionsList] common_usage: ClassVar[str] display_options: ClassVar[_OptionsList] display_option_names: ClassVar[list[str]] negative_opt: ClassVar[dict[str, str]] - verbose: int - dry_run: int - help: int + verbose: Literal[0, 1] + dry_run: Literal[0, 1] + help: Literal[0, 1] command_packages: list[str] | None script_name: str | None script_args: list[str] | None @@ -108,8 +124,137 @@ class Distribution: def print_commands(self) -> None: ... def get_command_list(self): ... def get_command_packages(self): ... + # NOTE: This list comes directly from the distutils/command folder. Minus bdist_msi and bdist_wininst. + @overload + def get_command_obj(self, command: Literal["bdist"], create: Literal[1, True] = 1) -> bdist: ... + @overload + def get_command_obj(self, command: Literal["bdist_dumb"], create: Literal[1, True] = 1) -> bdist_dumb: ... + @overload + def get_command_obj(self, command: Literal["bdist_rpm"], create: Literal[1, True] = 1) -> bdist_rpm: ... + @overload + def get_command_obj(self, command: Literal["build"], create: Literal[1, True] = 1) -> build: ... + @overload + def get_command_obj(self, command: Literal["build_clib"], create: Literal[1, True] = 1) -> build_clib: ... + @overload + def get_command_obj(self, command: Literal["build_ext"], create: Literal[1, True] = 1) -> build_ext: ... + @overload + def get_command_obj(self, command: Literal["build_py"], create: Literal[1, True] = 1) -> build_py: ... + @overload + def get_command_obj(self, command: Literal["build_scripts"], create: Literal[1, True] = 1) -> build_scripts: ... + @overload + def get_command_obj(self, command: Literal["check"], create: Literal[1, True] = 1) -> check: ... + @overload + def get_command_obj(self, command: Literal["clean"], create: Literal[1, True] = 1) -> clean: ... + @overload + def get_command_obj(self, command: Literal["config"], create: Literal[1, True] = 1) -> config: ... + @overload + def get_command_obj(self, command: Literal["install"], create: Literal[1, True] = 1) -> install: ... + @overload + def get_command_obj(self, command: Literal["install_data"], create: Literal[1, True] = 1) -> install_data: ... + @overload + def get_command_obj(self, command: Literal["install_egg_info"], create: Literal[1, True] = 1) -> install_egg_info: ... + @overload + def get_command_obj(self, command: Literal["install_headers"], create: Literal[1, True] = 1) -> install_headers: ... + @overload + def get_command_obj(self, command: Literal["install_lib"], create: Literal[1, True] = 1) -> install_lib: ... + @overload + def get_command_obj(self, command: Literal["install_scripts"], create: Literal[1, True] = 1) -> install_scripts: ... + @overload + def get_command_obj(self, command: Literal["register"], create: Literal[1, True] = 1) -> register: ... + @overload + def get_command_obj(self, command: Literal["sdist"], create: Literal[1, True] = 1) -> sdist: ... + @overload + def get_command_obj(self, command: Literal["upload"], create: Literal[1, True] = 1) -> upload: ... + @overload + def get_command_obj(self, command: str, create: Literal[1, True] = 1) -> Command: ... + # Not replicating the overloads for "Command | None", user may use "isinstance" + @overload + def get_command_obj(self, command: str, create: Literal[0, False]) -> Command | None: ... + @overload + def get_command_class(self, command: Literal["bdist"]) -> type[bdist]: ... + @overload + def get_command_class(self, command: Literal["bdist_dumb"]) -> type[bdist_dumb]: ... + @overload + def get_command_class(self, command: Literal["bdist_rpm"]) -> type[bdist_rpm]: ... + @overload + def get_command_class(self, command: Literal["build"]) -> type[build]: ... + @overload + def get_command_class(self, command: Literal["build_clib"]) -> type[build_clib]: ... + @overload + def get_command_class(self, command: Literal["build_ext"]) -> type[build_ext]: ... + @overload + def get_command_class(self, command: Literal["build_py"]) -> type[build_py]: ... + @overload + def get_command_class(self, command: Literal["build_scripts"]) -> type[build_scripts]: ... + @overload + def get_command_class(self, command: Literal["check"]) -> type[check]: ... + @overload + def get_command_class(self, command: Literal["clean"]) -> type[clean]: ... + @overload + def get_command_class(self, command: Literal["config"]) -> type[config]: ... + @overload + def get_command_class(self, command: Literal["install"]) -> type[install]: ... + @overload + def get_command_class(self, command: Literal["install_data"]) -> type[install_data]: ... + @overload + def get_command_class(self, command: Literal["install_egg_info"]) -> type[install_egg_info]: ... + @overload + def get_command_class(self, command: Literal["install_headers"]) -> type[install_headers]: ... + @overload + def get_command_class(self, command: Literal["install_lib"]) -> type[install_lib]: ... + @overload + def get_command_class(self, command: Literal["install_scripts"]) -> type[install_scripts]: ... + @overload + def get_command_class(self, command: Literal["register"]) -> type[register]: ... + @overload + def get_command_class(self, command: Literal["sdist"]) -> type[sdist]: ... + @overload + def get_command_class(self, command: Literal["upload"]) -> type[upload]: ... + @overload def get_command_class(self, command: str) -> type[Command]: ... @overload + def reinitialize_command(self, command: Literal["bdist"], reinit_subcommands: bool = False) -> bdist: ... + @overload + def reinitialize_command(self, command: Literal["bdist_dumb"], reinit_subcommands: bool = False) -> bdist_dumb: ... + @overload + def reinitialize_command(self, command: Literal["bdist_rpm"], reinit_subcommands: bool = False) -> bdist_rpm: ... + @overload + def reinitialize_command(self, command: Literal["build"], reinit_subcommands: bool = False) -> build: ... + @overload + def reinitialize_command(self, command: Literal["build_clib"], reinit_subcommands: bool = False) -> build_clib: ... + @overload + def reinitialize_command(self, command: Literal["build_ext"], reinit_subcommands: bool = False) -> build_ext: ... + @overload + def reinitialize_command(self, command: Literal["build_py"], reinit_subcommands: bool = False) -> build_py: ... + @overload + def reinitialize_command(self, command: Literal["build_scripts"], reinit_subcommands: bool = False) -> build_scripts: ... + @overload + def reinitialize_command(self, command: Literal["check"], reinit_subcommands: bool = False) -> check: ... + @overload + def reinitialize_command(self, command: Literal["clean"], reinit_subcommands: bool = False) -> clean: ... + @overload + def reinitialize_command(self, command: Literal["config"], reinit_subcommands: bool = False) -> config: ... + @overload + def reinitialize_command(self, command: Literal["install"], reinit_subcommands: bool = False) -> install: ... + @overload + def reinitialize_command(self, command: Literal["install_data"], reinit_subcommands: bool = False) -> install_data: ... + @overload + def reinitialize_command( + self, command: Literal["install_egg_info"], reinit_subcommands: bool = False + ) -> install_egg_info: ... + @overload + def reinitialize_command(self, command: Literal["install_headers"], reinit_subcommands: bool = False) -> install_headers: ... + @overload + def reinitialize_command(self, command: Literal["install_lib"], reinit_subcommands: bool = False) -> install_lib: ... + @overload + def reinitialize_command(self, command: Literal["install_scripts"], reinit_subcommands: bool = False) -> install_scripts: ... + @overload + def reinitialize_command(self, command: Literal["register"], reinit_subcommands: bool = False) -> register: ... + @overload + def reinitialize_command(self, command: Literal["sdist"], reinit_subcommands: bool = False) -> sdist: ... + @overload + def reinitialize_command(self, command: Literal["upload"], reinit_subcommands: bool = False) -> upload: ... + @overload def reinitialize_command(self, command: str, reinit_subcommands: bool = False) -> Command: ... @overload def reinitialize_command(self, command: _CommandT, reinit_subcommands: bool = False) -> _CommandT: ... @@ -125,7 +270,7 @@ class Distribution: def has_data_files(self) -> bool: ... def is_pure(self) -> bool: ... - # Getter methods generated in __init__ + # Default getter methods generated in __init__ from self.metadata._METHOD_BASENAMES def get_name(self) -> str: ... def get_version(self) -> str: ... def get_fullname(self) -> str: ... @@ -147,3 +292,26 @@ class Distribution: def get_requires(self) -> list[str]: ... def get_provides(self) -> list[str]: ... def get_obsoletes(self) -> list[str]: ... + + # Default attributes generated in __init__ from self.display_option_names + help_commands: bool | Literal[0] + name: str | Literal[0] + version: str | Literal[0] + fullname: str | Literal[0] + author: str | Literal[0] + author_email: str | Literal[0] + maintainer: str | Literal[0] + maintainer_email: str | Literal[0] + contact: str | Literal[0] + contact_email: str | Literal[0] + url: str | Literal[0] + license: str | Literal[0] + licence: str | Literal[0] + description: str | Literal[0] + long_description: str | Literal[0] + platforms: str | list[str] | Literal[0] + classifiers: str | list[str] | Literal[0] + keywords: str | list[str] | Literal[0] + provides: list[str] | Literal[0] + requires: list[str] | Literal[0] + obsoletes: list[str] | Literal[0] diff --git a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi index f9916d4511b2f..c4d37419ed06c 100644 --- a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi +++ b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi @@ -1,15 +1,15 @@ from collections.abc import Iterable, Mapping from re import Pattern -from typing import Any, overload +from typing import Any, Final, overload from typing_extensions import TypeAlias _Option: TypeAlias = tuple[str, str | None, str] _GR: TypeAlias = tuple[list[str], OptionDummy] -longopt_pat: str -longopt_re: Pattern[str] -neg_alias_re: Pattern[str] -longopt_xlate: dict[int, int] +longopt_pat: Final = r"[a-zA-Z](?:[a-zA-Z0-9-]*)" +longopt_re: Final[Pattern[str]] +neg_alias_re: Final[Pattern[str]] +longopt_xlate: Final[dict[int, int]] class FancyGetopt: def __init__(self, option_table: list[_Option] | None = None) -> None: ... @@ -25,7 +25,7 @@ def fancy_getopt( options: list[_Option], negative_opt: Mapping[_Option, _Option], object: Any, args: list[str] | None ) -> list[str] | _GR: ... -WS_TRANS: dict[int, str] +WS_TRANS: Final[dict[int, str]] def wrap_text(text: str, width: int) -> list[str]: ... def translate_longopt(opt: str) -> str: ... diff --git a/mypy/typeshed/stdlib/distutils/log.pyi b/mypy/typeshed/stdlib/distutils/log.pyi index 14ed8d8aefa8e..7246dd6be0cdf 100644 --- a/mypy/typeshed/stdlib/distutils/log.pyi +++ b/mypy/typeshed/stdlib/distutils/log.pyi @@ -1,13 +1,14 @@ -from typing import Any +from typing import Any, Final -DEBUG: int -INFO: int -WARN: int -ERROR: int -FATAL: int +DEBUG: Final = 1 +INFO: Final = 2 +WARN: Final = 3 +ERROR: Final = 4 +FATAL: Final = 5 class Log: def __init__(self, threshold: int = 3) -> None: ... + # Arbitrary msg args' type depends on the format method def log(self, level: int, msg: str, *args: Any) -> None: ... def debug(self, msg: str, *args: Any) -> None: ... def info(self, msg: str, *args: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/spawn.pyi b/mypy/typeshed/stdlib/distutils/spawn.pyi index 50d89aeb9e5fd..ae07a49504fe1 100644 --- a/mypy/typeshed/stdlib/distutils/spawn.pyi +++ b/mypy/typeshed/stdlib/distutils/spawn.pyi @@ -1,6 +1,10 @@ +from collections.abc import Iterable from typing import Literal def spawn( - cmd: list[str], search_path: bool | Literal[0, 1] = 1, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0 + cmd: Iterable[str], + search_path: bool | Literal[0, 1] = 1, + verbose: bool | Literal[0, 1] = 0, + dry_run: bool | Literal[0, 1] = 0, ) -> None: ... def find_executable(executable: str, path: str | None = None) -> str | None: ... diff --git a/mypy/typeshed/stdlib/distutils/sysconfig.pyi b/mypy/typeshed/stdlib/distutils/sysconfig.pyi index da72e3275fe38..4a9c45eb562a4 100644 --- a/mypy/typeshed/stdlib/distutils/sysconfig.pyi +++ b/mypy/typeshed/stdlib/distutils/sysconfig.pyi @@ -1,15 +1,15 @@ import sys from collections.abc import Mapping from distutils.ccompiler import CCompiler -from typing import Literal, overload +from typing import Final, Literal, overload from typing_extensions import deprecated -PREFIX: str -EXEC_PREFIX: str -BASE_PREFIX: str -BASE_EXEC_PREFIX: str -project_base: str -python_build: bool +PREFIX: Final[str] +EXEC_PREFIX: Final[str] +BASE_PREFIX: Final[str] +BASE_EXEC_PREFIX: Final[str] +project_base: Final[str] +python_build: Final[bool] def expand_makefile_vars(s: str, vars: Mapping[str, str]) -> str: ... @overload diff --git a/mypy/typeshed/stdlib/distutils/util.pyi b/mypy/typeshed/stdlib/distutils/util.pyi index 515b5b2b86d9f..0e1bb4165d99d 100644 --- a/mypy/typeshed/stdlib/distutils/util.pyi +++ b/mypy/typeshed/stdlib/distutils/util.pyi @@ -1,6 +1,9 @@ from _typeshed import StrPath, Unused from collections.abc import Callable, Container, Iterable, Mapping from typing import Any, Literal +from typing_extensions import TypeVarTuple, Unpack + +_Ts = TypeVarTuple("_Ts") def get_host_platform() -> str: ... def get_platform() -> str: ... @@ -10,8 +13,8 @@ def check_environ() -> None: ... def subst_vars(s: str, local_vars: Mapping[str, str]) -> None: ... def split_quoted(s: str) -> list[str]: ... def execute( - func: Callable[..., object], - args: tuple[Any, ...], + func: Callable[[Unpack[_Ts]], Unused], + args: tuple[Unpack[_Ts]], msg: str | None = None, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0, diff --git a/mypy/typeshed/stdlib/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi index 7e334ef0c5044..562b5a5bdac9e 100644 --- a/mypy/typeshed/stdlib/doctest.pyi +++ b/mypy/typeshed/stdlib/doctest.pyi @@ -1,9 +1,10 @@ +import sys import types import unittest from _typeshed import ExcInfo from collections.abc import Callable -from typing import Any, NamedTuple -from typing_extensions import TypeAlias +from typing import Any, NamedTuple, type_check_only +from typing_extensions import Self, TypeAlias __all__ = [ "register_optionflag", @@ -41,9 +42,20 @@ __all__ = [ "debug", ] -class TestResults(NamedTuple): - failed: int - attempted: int +if sys.version_info >= (3, 13): + @type_check_only + class _TestResultsBase(NamedTuple): + failed: int + attempted: int + + class TestResults(_TestResultsBase): + def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... + skipped: int + +else: + class TestResults(NamedTuple): + failed: int + attempted: int OPTIONFLAGS_BY_NAME: dict[str, int] @@ -134,6 +146,8 @@ class DocTestRunner: original_optionflags: int tries: int failures: int + if sys.version_info >= (3, 13): + skips: int test: DocTest def __init__(self, checker: OutputChecker | None = None, verbose: bool | None = None, optionflags: int = 0) -> None: ... def report_start(self, out: _Out, test: DocTest, example: Example) -> None: ... diff --git a/mypy/typeshed/stdlib/email/__init__.pyi b/mypy/typeshed/stdlib/email/__init__.pyi index fca302f5f1a7d..f564ced105bd7 100644 --- a/mypy/typeshed/stdlib/email/__init__.pyi +++ b/mypy/typeshed/stdlib/email/__init__.pyi @@ -4,6 +4,29 @@ from email.policy import Policy from typing import IO from typing_extensions import TypeAlias +# At runtime, listing submodules in __all__ without them being imported is +# valid, and causes them to be included in a star import. See #6523 + +__all__ = [ # noqa: F822 # Undefined names in __all__ + "base64mime", # pyright: ignore[reportUnsupportedDunderAll] + "charset", # pyright: ignore[reportUnsupportedDunderAll] + "encoders", # pyright: ignore[reportUnsupportedDunderAll] + "errors", # pyright: ignore[reportUnsupportedDunderAll] + "feedparser", # pyright: ignore[reportUnsupportedDunderAll] + "generator", # pyright: ignore[reportUnsupportedDunderAll] + "header", # pyright: ignore[reportUnsupportedDunderAll] + "iterators", # pyright: ignore[reportUnsupportedDunderAll] + "message", # pyright: ignore[reportUnsupportedDunderAll] + "message_from_file", + "message_from_binary_file", + "message_from_string", + "message_from_bytes", + "mime", # pyright: ignore[reportUnsupportedDunderAll] + "parser", # pyright: ignore[reportUnsupportedDunderAll] + "quoprimime", # pyright: ignore[reportUnsupportedDunderAll] + "utils", # pyright: ignore[reportUnsupportedDunderAll] +] + # Definitions imported by multiple submodules in typeshed _ParamType: TypeAlias = str | tuple[str | None, str | None, str] # noqa: Y047 _ParamsType: TypeAlias = str | None | tuple[str, str | None, str] # noqa: Y047 @@ -12,18 +35,3 @@ def message_from_string(s: str, _class: Callable[[], Message] = ..., *, policy: def message_from_bytes(s: bytes | bytearray, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... def message_from_file(fp: IO[str], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... def message_from_binary_file(fp: IO[bytes], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... - -# Names in __all__ with no definition: -# base64mime -# charset -# encoders -# errors -# feedparser -# generator -# header -# iterators -# message -# mime -# parser -# quoprimime -# utils diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi index 806fc84cf784f..ff405a8b61d22 100644 --- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi +++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi @@ -16,6 +16,10 @@ TOKEN_ENDS: Final[set[str]] ASPECIALS: Final[set[str]] ATTRIBUTE_ENDS: Final[set[str]] EXTENDED_ATTRIBUTE_ENDS: Final[set[str]] +# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +NLSET: Final[set[str]] +# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +SPECIALSNL: Final[set[str]] def quote_string(value: Any) -> str: ... diff --git a/mypy/typeshed/stdlib/email/_policybase.pyi b/mypy/typeshed/stdlib/email/_policybase.pyi index a3dd61a282ce3..f5dbbd96da147 100644 --- a/mypy/typeshed/stdlib/email/_policybase.pyi +++ b/mypy/typeshed/stdlib/email/_policybase.pyi @@ -1,22 +1,31 @@ from abc import ABCMeta, abstractmethod -from collections.abc import Callable from email.errors import MessageDefect from email.header import Header from email.message import Message -from typing import Any +from typing import Generic, Protocol, TypeVar, type_check_only from typing_extensions import Self -class _PolicyBase: - def __add__(self, other: Any) -> Self: ... - def clone(self, **kw: Any) -> Self: ... +__all__ = ["Policy", "Compat32", "compat32"] -class Policy(_PolicyBase, metaclass=ABCMeta): +_MessageT = TypeVar("_MessageT", bound=Message, default=Message) + +@type_check_only +class _MessageFactory(Protocol[_MessageT]): + def __call__(self, policy: Policy[_MessageT]) -> _MessageT: ... + +# Policy below is the only known direct subclass of _PolicyBase. We therefore +# assume that the __init__ arguments and attributes of _PolicyBase are +# the same as those of Policy. +class _PolicyBase(Generic[_MessageT]): max_line_length: int | None linesep: str cte_type: str raise_on_defect: bool mangle_from_: bool - message_factory: Callable[[Policy], Message] | None + message_factory: _MessageFactory[_MessageT] | None + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool + def __init__( self, *, @@ -24,11 +33,28 @@ class Policy(_PolicyBase, metaclass=ABCMeta): linesep: str = "\n", cte_type: str = "8bit", raise_on_defect: bool = False, - mangle_from_: bool = False, - message_factory: Callable[[Policy], Message] | None = None, + mangle_from_: bool = ..., # default depends on sub-class + message_factory: _MessageFactory[_MessageT] | None = None, + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = True, ) -> None: ... - def handle_defect(self, obj: Message, defect: MessageDefect) -> None: ... - def register_defect(self, obj: Message, defect: MessageDefect) -> None: ... + def clone( + self, + *, + max_line_length: int | None = ..., + linesep: str = ..., + cte_type: str = ..., + raise_on_defect: bool = ..., + mangle_from_: bool = ..., + message_factory: _MessageFactory[_MessageT] | None = ..., + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., + ) -> Self: ... + def __add__(self, other: Policy) -> Self: ... + +class Policy(_PolicyBase[_MessageT], metaclass=ABCMeta): + def handle_defect(self, obj: _MessageT, defect: MessageDefect) -> None: ... + def register_defect(self, obj: _MessageT, defect: MessageDefect) -> None: ... def header_max_count(self, name: str) -> int | None: ... @abstractmethod def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... @@ -41,11 +67,11 @@ class Policy(_PolicyBase, metaclass=ABCMeta): @abstractmethod def fold_binary(self, name: str, value: str) -> bytes: ... -class Compat32(Policy): +class Compat32(Policy[_MessageT]): def header_source_parse(self, sourcelines: list[str]) -> tuple[str, str]: ... def header_store_parse(self, name: str, value: str) -> tuple[str, str]: ... def header_fetch_parse(self, name: str, value: str) -> str | Header: ... # type: ignore[override] def fold(self, name: str, value: str) -> str: ... def fold_binary(self, name: str, value: str) -> bytes: ... -compat32: Compat32 +compat32: Compat32[Message] diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index 2d12df3372079..2939192c95264 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -1,12 +1,12 @@ from collections.abc import Callable, Iterator from email.message import Message -from typing import overload +from typing import Final, overload __all__ = ["Charset", "add_alias", "add_charset", "add_codec"] -QP: int # undocumented -BASE64: int # undocumented -SHORTEST: int # undocumented +QP: Final[int] # undocumented +BASE64: Final[int] # undocumented +SHORTEST: Final[int] # undocumented class Charset: input_charset: str diff --git a/mypy/typeshed/stdlib/email/errors.pyi b/mypy/typeshed/stdlib/email/errors.pyi index c54f1560c9aec..f105576c5ee49 100644 --- a/mypy/typeshed/stdlib/email/errors.pyi +++ b/mypy/typeshed/stdlib/email/errors.pyi @@ -7,6 +7,9 @@ class BoundaryError(MessageParseError): ... class MultipartConversionError(MessageError, TypeError): ... class CharsetError(MessageError): ... +# Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +class HeaderWriteError(MessageError): ... + class MessageDefect(ValueError): def __init__(self, line: str | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/email/feedparser.pyi b/mypy/typeshed/stdlib/email/feedparser.pyi index 22920fc99c69a..8c268ca1ae18c 100644 --- a/mypy/typeshed/stdlib/email/feedparser.pyi +++ b/mypy/typeshed/stdlib/email/feedparser.pyi @@ -5,19 +5,19 @@ from typing import Generic, TypeVar, overload __all__ = ["FeedParser", "BytesFeedParser"] -_MessageT = TypeVar("_MessageT", bound=Message) +_MessageT = TypeVar("_MessageT", bound=Message, default=Message) class FeedParser(Generic[_MessageT]): @overload - def __init__(self: FeedParser[Message], _factory: None = None, *, policy: Policy = ...) -> None: ... + def __init__(self: FeedParser[Message], _factory: None = None, *, policy: Policy[Message] = ...) -> None: ... @overload - def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def feed(self, data: str) -> None: ... def close(self) -> _MessageT: ... class BytesFeedParser(FeedParser[_MessageT]): @overload - def __init__(self: BytesFeedParser[Message], _factory: None = None, *, policy: Policy = ...) -> None: ... + def __init__(self: BytesFeedParser[Message], _factory: None = None, *, policy: Policy[Message] = ...) -> None: ... @overload - def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _factory: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def feed(self, data: bytes | bytearray) -> None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/email/generator.pyi b/mypy/typeshed/stdlib/email/generator.pyi index faa6551fc925e..dfa0604a20a98 100644 --- a/mypy/typeshed/stdlib/email/generator.pyi +++ b/mypy/typeshed/stdlib/email/generator.pyi @@ -1,34 +1,71 @@ from _typeshed import SupportsWrite from email.message import Message from email.policy import Policy +from typing import Any, Generic, TypeVar, overload from typing_extensions import Self __all__ = ["Generator", "DecodedGenerator", "BytesGenerator"] -class Generator: - def clone(self, fp: SupportsWrite[str]) -> Self: ... - def write(self, s: str) -> None: ... +# By default, generators do not have a message policy. +_MessageT = TypeVar("_MessageT", bound=Message, default=Any) + +class Generator(Generic[_MessageT]): + maxheaderlen: int | None + policy: Policy[_MessageT] | None + @overload + def __init__( + self: Generator[Any], # The Policy of the message is used. + outfp: SupportsWrite[str], + mangle_from_: bool | None = None, + maxheaderlen: int | None = None, + *, + policy: None = None, + ) -> None: ... + @overload def __init__( self, outfp: SupportsWrite[str], mangle_from_: bool | None = None, maxheaderlen: int | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT], ) -> None: ... - def flatten(self, msg: Message, unixfrom: bool = False, linesep: str | None = None) -> None: ... + def write(self, s: str) -> None: ... + def flatten(self, msg: _MessageT, unixfrom: bool = False, linesep: str | None = None) -> None: ... + def clone(self, fp: SupportsWrite[str]) -> Self: ... -class BytesGenerator(Generator): +class BytesGenerator(Generator[_MessageT]): + @overload + def __init__( + self: BytesGenerator[Any], # The Policy of the message is used. + outfp: SupportsWrite[bytes], + mangle_from_: bool | None = None, + maxheaderlen: int | None = None, + *, + policy: None = None, + ) -> None: ... + @overload def __init__( self, outfp: SupportsWrite[bytes], mangle_from_: bool | None = None, maxheaderlen: int | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT], ) -> None: ... -class DecodedGenerator(Generator): +class DecodedGenerator(Generator[_MessageT]): + @overload + def __init__( + self: DecodedGenerator[Any], # The Policy of the message is used. + outfp: SupportsWrite[str], + mangle_from_: bool | None = None, + maxheaderlen: int | None = None, + fmt: str | None = None, + *, + policy: None = None, + ) -> None: ... + @overload def __init__( self, outfp: SupportsWrite[str], @@ -36,5 +73,5 @@ class DecodedGenerator(Generator): maxheaderlen: int | None = None, fmt: str | None = None, *, - policy: Policy | None = None, + policy: Policy[_MessageT], ) -> None: ... diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 4032bc6136d46..ebad05a1cf7b6 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -1,3 +1,4 @@ +from _typeshed import MaybeNone from collections.abc import Generator, Iterator, Sequence from email import _ParamsType, _ParamType from email.charset import Charset @@ -30,27 +31,31 @@ class _SupportsDecodeToPayload(Protocol): def decode(self, encoding: str, errors: str, /) -> _PayloadType | _MultipartPayloadType: ... class Message(Generic[_HeaderT, _HeaderParamT]): - policy: Policy # undocumented + # The policy attributes and arguments in this class and its subclasses + # would ideally use Policy[Self], but this is not possible. + policy: Policy[Any] # undocumented preamble: str | None epilogue: str | None defects: list[MessageDefect] + def __init__(self, policy: Policy[Any] = ...) -> None: ... def is_multipart(self) -> bool: ... def set_unixfrom(self, unixfrom: str) -> None: ... def get_unixfrom(self) -> str | None: ... def attach(self, payload: _PayloadType) -> None: ... # `i: int` without a multipart payload results in an error - # `| Any`: can be None for cleared or unset payload, but annoying to check + # `| MaybeNone` acts like `| Any`: can be None for cleared or unset payload, but annoying to check @overload # multipart def get_payload(self, i: int, decode: Literal[True]) -> None: ... @overload # multipart - def get_payload(self, i: int, decode: Literal[False] = False) -> _PayloadType | Any: ... + def get_payload(self, i: int, decode: Literal[False] = False) -> _PayloadType | MaybeNone: ... @overload # either - def get_payload(self, i: None = None, decode: Literal[False] = False) -> _PayloadType | _MultipartPayloadType | Any: ... + def get_payload(self, i: None = None, decode: Literal[False] = False) -> _PayloadType | _MultipartPayloadType | MaybeNone: ... @overload # not multipart - def get_payload(self, i: None = None, *, decode: Literal[True]) -> _EncodedPayloadType | Any: ... + def get_payload(self, i: None = None, *, decode: Literal[True]) -> _EncodedPayloadType | MaybeNone: ... @overload # not multipart, IDEM but w/o kwarg - def get_payload(self, i: None, decode: Literal[True]) -> _EncodedPayloadType | Any: ... - # If `charset=None` and payload supports both `encode` AND `decode`, then an invalid payload could be passed, but this is unlikely + def get_payload(self, i: None, decode: Literal[True]) -> _EncodedPayloadType | MaybeNone: ... + # If `charset=None` and payload supports both `encode` AND `decode`, + # then an invalid payload could be passed, but this is unlikely # Not[_SupportsEncodeToPayload] @overload def set_payload( @@ -71,7 +76,7 @@ class Message(Generic[_HeaderT, _HeaderParamT]): # This is important for protocols using __getitem__, like SupportsKeysAndGetItem # Morally, the return type should be `AnyOf[_HeaderType, None]`, # so using "the Any trick" instead. - def __getitem__(self, name: str) -> _HeaderT | Any: ... + def __getitem__(self, name: str) -> _HeaderT | MaybeNone: ... def __setitem__(self, name: str, val: _HeaderParamT) -> None: ... def __delitem__(self, name: str) -> None: ... def keys(self) -> list[str]: ... @@ -125,8 +130,8 @@ class Message(Generic[_HeaderT, _HeaderParamT]): def get_charsets(self, failobj: _T) -> list[str | _T]: ... def walk(self) -> Generator[Self, None, None]: ... def get_content_disposition(self) -> str | None: ... - def as_string(self, unixfrom: bool = False, maxheaderlen: int = 0, policy: Policy | None = None) -> str: ... - def as_bytes(self, unixfrom: bool = False, policy: Policy | None = None) -> bytes: ... + def as_string(self, unixfrom: bool = False, maxheaderlen: int = 0, policy: Policy[Any] | None = None) -> str: ... + def as_bytes(self, unixfrom: bool = False, policy: Policy[Any] | None = None) -> bytes: ... def __bytes__(self) -> bytes: ... def set_param( self, @@ -138,15 +143,18 @@ class Message(Generic[_HeaderT, _HeaderParamT]): language: str = "", replace: bool = False, ) -> None: ... - def __init__(self, policy: Policy = ...) -> None: ... # The following two methods are undocumented, but a source code comment states that they are public API def set_raw(self, name: str, value: _HeaderParamT) -> None: ... def raw_items(self) -> Iterator[tuple[str, _HeaderT]]: ... class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): - def __init__(self, policy: Policy | None = None) -> None: ... + def __init__(self, policy: Policy[Any] | None = None) -> None: ... def get_body(self, preferencelist: Sequence[str] = ("related", "html", "plain")) -> MIMEPart[_HeaderRegistryT] | None: ... - def iter_attachments(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... + def attach(self, payload: Self) -> None: ... # type: ignore[override] + # The attachments are created via type(self) in the attach method. It's theoretically + # possible to sneak other attachment types into a MIMEPart instance, but could cause + # cause unforseen consequences. + def iter_attachments(self) -> Iterator[Self]: ... def iter_parts(self) -> Iterator[MIMEPart[_HeaderRegistryT]]: ... def get_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> Any: ... def set_content(self, *args: Any, content_manager: ContentManager | None = None, **kw: Any) -> None: ... @@ -158,7 +166,7 @@ class MIMEPart(Message[_HeaderRegistryT, _HeaderRegistryParamT]): def add_attachment(self, *args: Any, content_manager: ContentManager | None = ..., **kw: Any) -> None: ... def clear(self) -> None: ... def clear_content(self) -> None: ... - def as_string(self, unixfrom: bool = False, maxheaderlen: int | None = None, policy: Policy | None = None) -> str: ... + def as_string(self, unixfrom: bool = False, maxheaderlen: int | None = None, policy: Policy[Any] | None = None) -> str: ... def is_attachment(self) -> bool: ... class EmailMessage(MIMEPart): ... diff --git a/mypy/typeshed/stdlib/email/parser.pyi b/mypy/typeshed/stdlib/email/parser.pyi index fecb29d90b2e4..a1a57b4eef4b1 100644 --- a/mypy/typeshed/stdlib/email/parser.pyi +++ b/mypy/typeshed/stdlib/email/parser.pyi @@ -12,9 +12,9 @@ _MessageT = TypeVar("_MessageT", bound=Message, default=Message) class Parser(Generic[_MessageT]): @overload - def __init__(self: Parser[Message[str, str]], _class: None = None, *, policy: Policy = ...) -> None: ... + def __init__(self: Parser[Message[str, str]], _class: None = None, *, policy: Policy[Message[str, str]] = ...) -> None: ... @overload - def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def parse(self, fp: SupportsRead[str], headersonly: bool = False) -> _MessageT: ... def parsestr(self, text: str, headersonly: bool = False) -> _MessageT: ... @@ -25,9 +25,11 @@ class HeaderParser(Parser[_MessageT]): class BytesParser(Generic[_MessageT]): parser: Parser[_MessageT] @overload - def __init__(self: BytesParser[Message[str, str]], _class: None = None, *, policy: Policy = ...) -> None: ... + def __init__( + self: BytesParser[Message[str, str]], _class: None = None, *, policy: Policy[Message[str, str]] = ... + ) -> None: ... @overload - def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy = ...) -> None: ... + def __init__(self, _class: Callable[[], _MessageT], *, policy: Policy[_MessageT] = ...) -> None: ... def parse(self, fp: _WrappedBuffer, headersonly: bool = False) -> _MessageT: ... def parsebytes(self, text: bytes | bytearray, headersonly: bool = False) -> _MessageT: ... diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi index 5f1cf934eb3c0..5b145bcf23180 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -1,16 +1,37 @@ from collections.abc import Callable -from email._policybase import Compat32 as Compat32, Policy as Policy, compat32 as compat32 +from email._policybase import Compat32 as Compat32, Policy as Policy, _MessageFactory, compat32 as compat32 from email.contentmanager import ContentManager -from email.message import Message -from typing import Any +from email.message import EmailMessage, Message +from typing import Any, TypeVar, overload +from typing_extensions import Self __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"] -class EmailPolicy(Policy): +_MessageT = TypeVar("_MessageT", bound=Message, default=Message) + +class EmailPolicy(Policy[_MessageT]): utf8: bool refold_source: str header_factory: Callable[[str, Any], Any] content_manager: ContentManager + @overload + def __init__( + self: EmailPolicy[EmailMessage], + *, + max_line_length: int | None = ..., + linesep: str = ..., + cte_type: str = ..., + raise_on_defect: bool = ..., + mangle_from_: bool = ..., + message_factory: None = None, + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., + utf8: bool = ..., + refold_source: str = ..., + header_factory: Callable[[str, str], str] = ..., + content_manager: ContentManager = ..., + ) -> None: ... + @overload def __init__( self, *, @@ -19,7 +40,9 @@ class EmailPolicy(Policy): cte_type: str = ..., raise_on_defect: bool = ..., mangle_from_: bool = ..., - message_factory: Callable[[Policy], Message] | None = ..., + message_factory: _MessageFactory[_MessageT] | None = ..., + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., utf8: bool = ..., refold_source: str = ..., header_factory: Callable[[str, str], str] = ..., @@ -30,9 +53,25 @@ class EmailPolicy(Policy): def header_fetch_parse(self, name: str, value: str) -> Any: ... def fold(self, name: str, value: str) -> Any: ... def fold_binary(self, name: str, value: str) -> bytes: ... + def clone( + self, + *, + max_line_length: int | None = ..., + linesep: str = ..., + cte_type: str = ..., + raise_on_defect: bool = ..., + mangle_from_: bool = ..., + message_factory: _MessageFactory[_MessageT] | None = ..., + # Added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 + verify_generated_headers: bool = ..., + utf8: bool = ..., + refold_source: str = ..., + header_factory: Callable[[str, str], str] = ..., + content_manager: ContentManager = ..., + ) -> Self: ... -default: EmailPolicy -SMTP: EmailPolicy -SMTPUTF8: EmailPolicy -HTTP: EmailPolicy -strict: EmailPolicy +default: EmailPolicy[EmailMessage] +SMTP: EmailPolicy[EmailMessage] +SMTPUTF8: EmailPolicy[EmailMessage] +HTTP: EmailPolicy[EmailMessage] +strict: EmailPolicy[EmailMessage] diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index 0b62647532db2..dc3eecb5ef7fb 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -1,6 +1,7 @@ import datetime import sys from _typeshed import Unused +from collections.abc import Iterable from email import _ParamType from email.charset import Charset from typing import overload @@ -28,9 +29,13 @@ _PDTZ: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int | None def quote(str: str) -> str: ... def unquote(str: str) -> str: ... -def parseaddr(addr: str | None) -> tuple[str, str]: ... + +# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +def parseaddr(addr: str | list[str], *, strict: bool = True) -> tuple[str, str]: ... def formataddr(pair: tuple[str | None, str], charset: str | Charset = "utf-8") -> str: ... -def getaddresses(fieldvalues: list[str]) -> list[tuple[str, str]]: ... + +# `strict` parameter added in Python 3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.5 +def getaddresses(fieldvalues: Iterable[str], *, strict: bool = True) -> list[tuple[str, str]]: ... @overload def parsedate(data: None) -> None: ... @overload @@ -53,7 +58,10 @@ def mktime_tz(data: _PDTZ) -> int: ... def formatdate(timeval: float | None = None, localtime: bool = False, usegmt: bool = False) -> str: ... def format_datetime(dt: datetime.datetime, usegmt: bool = False) -> str: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 14): + def localtime(dt: datetime.datetime | None = None) -> datetime.datetime: ... + +elif sys.version_info >= (3, 12): @overload def localtime(dt: datetime.datetime | None = None) -> datetime.datetime: ... @overload diff --git a/mypy/typeshed/stdlib/encodings/aliases.pyi b/mypy/typeshed/stdlib/encodings/aliases.pyi new file mode 100644 index 0000000000000..079af85d51ee4 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/aliases.pyi @@ -0,0 +1 @@ +aliases: dict[str, str] diff --git a/mypy/typeshed/stdlib/encodings/ascii.pyi b/mypy/typeshed/stdlib/encodings/ascii.pyi new file mode 100644 index 0000000000000..a85585af32ed9 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/ascii.pyi @@ -0,0 +1,30 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.ascii_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.ascii_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +# Note: encode being a decode function and decode being an encode function is accurate to runtime. +class StreamConverter(StreamWriter, StreamReader): # type: ignore[misc] # incompatible methods in base classes + # At runtime, this is codecs.ascii_decode + @staticmethod + def encode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... # type: ignore[override] + # At runtime, this is codecs.ascii_encode + @staticmethod + def decode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/base64_codec.pyi b/mypy/typeshed/stdlib/encodings/base64_codec.pyi new file mode 100644 index 0000000000000..0c4f1cb1fe599 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/base64_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def base64_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def base64_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/big5.pyi b/mypy/typeshed/stdlib/encodings/big5.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/big5.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/big5hkscs.pyi b/mypy/typeshed/stdlib/encodings/big5hkscs.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/big5hkscs.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/bz2_codec.pyi b/mypy/typeshed/stdlib/encodings/bz2_codec.pyi new file mode 100644 index 0000000000000..468346a93da98 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/bz2_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def bz2_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def bz2_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/charmap.pyi b/mypy/typeshed/stdlib/encodings/charmap.pyi new file mode 100644 index 0000000000000..a971a15860b52 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/charmap.pyi @@ -0,0 +1,33 @@ +import codecs +from _codecs import _CharMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.charmap_encode + @staticmethod + def encode(str: str, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.charmap_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + mapping: _CharMap | None + def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + mapping: _CharMap | None + def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): + mapping: _CharMap | None + def __init__(self, stream: codecs._WritableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class StreamReader(Codec, codecs.StreamReader): + mapping: _CharMap | None + def __init__(self, stream: codecs._ReadableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ... + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/cp037.pyi b/mypy/typeshed/stdlib/encodings/cp037.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp037.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1006.pyi b/mypy/typeshed/stdlib/encodings/cp1006.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1006.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1026.pyi b/mypy/typeshed/stdlib/encodings/cp1026.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1026.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1125.pyi b/mypy/typeshed/stdlib/encodings/cp1125.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1125.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp1140.pyi b/mypy/typeshed/stdlib/encodings/cp1140.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1140.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1250.pyi b/mypy/typeshed/stdlib/encodings/cp1250.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1250.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1251.pyi b/mypy/typeshed/stdlib/encodings/cp1251.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1251.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1252.pyi b/mypy/typeshed/stdlib/encodings/cp1252.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1252.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1253.pyi b/mypy/typeshed/stdlib/encodings/cp1253.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1253.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1254.pyi b/mypy/typeshed/stdlib/encodings/cp1254.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1254.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1255.pyi b/mypy/typeshed/stdlib/encodings/cp1255.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1255.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1256.pyi b/mypy/typeshed/stdlib/encodings/cp1256.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1256.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1257.pyi b/mypy/typeshed/stdlib/encodings/cp1257.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1257.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp1258.pyi b/mypy/typeshed/stdlib/encodings/cp1258.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp1258.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp273.pyi b/mypy/typeshed/stdlib/encodings/cp273.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp273.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp424.pyi b/mypy/typeshed/stdlib/encodings/cp424.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp424.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp437.pyi b/mypy/typeshed/stdlib/encodings/cp437.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp437.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp500.pyi b/mypy/typeshed/stdlib/encodings/cp500.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp500.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp720.pyi b/mypy/typeshed/stdlib/encodings/cp720.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp720.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp737.pyi b/mypy/typeshed/stdlib/encodings/cp737.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp737.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp775.pyi b/mypy/typeshed/stdlib/encodings/cp775.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp775.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp850.pyi b/mypy/typeshed/stdlib/encodings/cp850.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp850.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp852.pyi b/mypy/typeshed/stdlib/encodings/cp852.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp852.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp855.pyi b/mypy/typeshed/stdlib/encodings/cp855.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp855.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp856.pyi b/mypy/typeshed/stdlib/encodings/cp856.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp856.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp857.pyi b/mypy/typeshed/stdlib/encodings/cp857.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp857.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp858.pyi b/mypy/typeshed/stdlib/encodings/cp858.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp858.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp860.pyi b/mypy/typeshed/stdlib/encodings/cp860.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp860.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp861.pyi b/mypy/typeshed/stdlib/encodings/cp861.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp861.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp862.pyi b/mypy/typeshed/stdlib/encodings/cp862.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp862.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp863.pyi b/mypy/typeshed/stdlib/encodings/cp863.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp863.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp864.pyi b/mypy/typeshed/stdlib/encodings/cp864.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp864.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp865.pyi b/mypy/typeshed/stdlib/encodings/cp865.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp865.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp866.pyi b/mypy/typeshed/stdlib/encodings/cp866.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp866.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp869.pyi b/mypy/typeshed/stdlib/encodings/cp869.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp869.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/cp874.pyi b/mypy/typeshed/stdlib/encodings/cp874.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp874.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp875.pyi b/mypy/typeshed/stdlib/encodings/cp875.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp875.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/cp932.pyi b/mypy/typeshed/stdlib/encodings/cp932.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp932.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/cp949.pyi b/mypy/typeshed/stdlib/encodings/cp949.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp949.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/cp950.pyi b/mypy/typeshed/stdlib/encodings/cp950.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/cp950.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_jis_2004.pyi b/mypy/typeshed/stdlib/encodings/euc_jis_2004.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_jis_2004.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_jisx0213.pyi b/mypy/typeshed/stdlib/encodings/euc_jisx0213.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_jisx0213.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_jp.pyi b/mypy/typeshed/stdlib/encodings/euc_jp.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_jp.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/euc_kr.pyi b/mypy/typeshed/stdlib/encodings/euc_kr.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/euc_kr.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/gb18030.pyi b/mypy/typeshed/stdlib/encodings/gb18030.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/gb18030.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/gb2312.pyi b/mypy/typeshed/stdlib/encodings/gb2312.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/gb2312.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/gbk.pyi b/mypy/typeshed/stdlib/encodings/gbk.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/gbk.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/hex_codec.pyi b/mypy/typeshed/stdlib/encodings/hex_codec.pyi new file mode 100644 index 0000000000000..3fd4fe38898a8 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/hex_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def hex_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def hex_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/hp_roman8.pyi b/mypy/typeshed/stdlib/encodings/hp_roman8.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/hp_roman8.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/hz.pyi b/mypy/typeshed/stdlib/encodings/hz.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/hz.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/idna.pyi b/mypy/typeshed/stdlib/encodings/idna.pyi new file mode 100644 index 0000000000000..3e2c8baf1cb28 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/idna.pyi @@ -0,0 +1,26 @@ +import codecs +import re +from _typeshed import ReadableBuffer + +dots: re.Pattern[str] +ace_prefix: bytes +sace_prefix: str + +def nameprep(label: str) -> str: ... +def ToASCII(label: str) -> bytes: ... +def ToUnicode(label: bytes | str) -> str: ... + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: ReadableBuffer | str, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, input: str, errors: str, final: bool) -> tuple[bytes, int]: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: ReadableBuffer | str, errors: str, final: bool) -> tuple[str, int]: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_1.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_1.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_1.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_2.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_2.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_2.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_2004.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_2004.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_2004.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_3.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_3.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_3.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_jp_ext.pyi b/mypy/typeshed/stdlib/encodings/iso2022_jp_ext.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_jp_ext.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso2022_kr.pyi b/mypy/typeshed/stdlib/encodings/iso2022_kr.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso2022_kr.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/iso8859_1.pyi b/mypy/typeshed/stdlib/encodings/iso8859_1.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_1.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_10.pyi b/mypy/typeshed/stdlib/encodings/iso8859_10.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_10.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_11.pyi b/mypy/typeshed/stdlib/encodings/iso8859_11.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_11.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_13.pyi b/mypy/typeshed/stdlib/encodings/iso8859_13.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_13.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_14.pyi b/mypy/typeshed/stdlib/encodings/iso8859_14.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_14.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_15.pyi b/mypy/typeshed/stdlib/encodings/iso8859_15.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_15.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_16.pyi b/mypy/typeshed/stdlib/encodings/iso8859_16.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_16.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_2.pyi b/mypy/typeshed/stdlib/encodings/iso8859_2.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_2.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_3.pyi b/mypy/typeshed/stdlib/encodings/iso8859_3.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_3.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_4.pyi b/mypy/typeshed/stdlib/encodings/iso8859_4.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_4.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_5.pyi b/mypy/typeshed/stdlib/encodings/iso8859_5.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_5.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_6.pyi b/mypy/typeshed/stdlib/encodings/iso8859_6.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_6.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_7.pyi b/mypy/typeshed/stdlib/encodings/iso8859_7.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_7.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_8.pyi b/mypy/typeshed/stdlib/encodings/iso8859_8.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_8.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/iso8859_9.pyi b/mypy/typeshed/stdlib/encodings/iso8859_9.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/iso8859_9.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/johab.pyi b/mypy/typeshed/stdlib/encodings/johab.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/johab.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/koi8_r.pyi b/mypy/typeshed/stdlib/encodings/koi8_r.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/koi8_r.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/koi8_t.pyi b/mypy/typeshed/stdlib/encodings/koi8_t.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/koi8_t.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/koi8_u.pyi b/mypy/typeshed/stdlib/encodings/koi8_u.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/koi8_u.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/kz1048.pyi b/mypy/typeshed/stdlib/encodings/kz1048.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/kz1048.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/latin_1.pyi b/mypy/typeshed/stdlib/encodings/latin_1.pyi new file mode 100644 index 0000000000000..3b06773eac03c --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/latin_1.pyi @@ -0,0 +1,30 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.latin_1_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.latin_1_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +# Note: encode being a decode function and decode being an encode function is accurate to runtime. +class StreamConverter(StreamWriter, StreamReader): # type: ignore[misc] # incompatible methods in base classes + # At runtime, this is codecs.latin_1_decode + @staticmethod + def encode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... # type: ignore[override] + # At runtime, this is codecs.latin_1_encode + @staticmethod + def decode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/mac_arabic.pyi b/mypy/typeshed/stdlib/encodings/mac_arabic.pyi new file mode 100644 index 0000000000000..42781b4892984 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_arabic.pyi @@ -0,0 +1,21 @@ +import codecs +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_map: dict[int, int | None] +decoding_table: str +encoding_map: dict[int, int] diff --git a/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi b/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_centeuro.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_croatian.pyi b/mypy/typeshed/stdlib/encodings/mac_croatian.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_croatian.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_cyrillic.pyi b/mypy/typeshed/stdlib/encodings/mac_cyrillic.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_cyrillic.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_farsi.pyi b/mypy/typeshed/stdlib/encodings/mac_farsi.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_farsi.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_greek.pyi b/mypy/typeshed/stdlib/encodings/mac_greek.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_greek.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_iceland.pyi b/mypy/typeshed/stdlib/encodings/mac_iceland.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_iceland.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_latin2.pyi b/mypy/typeshed/stdlib/encodings/mac_latin2.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_latin2.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_roman.pyi b/mypy/typeshed/stdlib/encodings/mac_roman.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_roman.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_romanian.pyi b/mypy/typeshed/stdlib/encodings/mac_romanian.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_romanian.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mac_turkish.pyi b/mypy/typeshed/stdlib/encodings/mac_turkish.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mac_turkish.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/mbcs.pyi b/mypy/typeshed/stdlib/encodings/mbcs.pyi new file mode 100644 index 0000000000000..2c2917d63f6db --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/mbcs.pyi @@ -0,0 +1,28 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +if sys.platform == "win32": + encode = codecs.mbcs_encode + + def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + + class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.mbcs_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.mbcs_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + + class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.mbcs_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/oem.pyi b/mypy/typeshed/stdlib/encodings/oem.pyi new file mode 100644 index 0000000000000..376c12c445f42 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/oem.pyi @@ -0,0 +1,28 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +if sys.platform == "win32": + encode = codecs.oem_encode + + def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + + class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.oem_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.oem_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + + class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.oem_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + + def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/palmos.pyi b/mypy/typeshed/stdlib/encodings/palmos.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/palmos.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/ptcp154.pyi b/mypy/typeshed/stdlib/encodings/ptcp154.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/ptcp154.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/punycode.pyi b/mypy/typeshed/stdlib/encodings/punycode.pyi new file mode 100644 index 0000000000000..eb99e667b4167 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/punycode.pyi @@ -0,0 +1,33 @@ +import codecs +from typing import Literal + +def segregate(str: str) -> tuple[bytes, list[int]]: ... +def selective_len(str: str, max: int) -> int: ... +def selective_find(str: str, char: str, index: int, pos: int) -> tuple[int, int]: ... +def insertion_unsort(str: str, extended: list[int]) -> list[int]: ... +def T(j: int, bias: int) -> int: ... + +digits: Literal[b"abcdefghijklmnopqrstuvwxyz0123456789"] + +def generate_generalized_integer(N: int, bias: int) -> bytes: ... +def adapt(delta: int, first: bool, numchars: int) -> int: ... +def generate_integers(baselen: int, deltas: list[int]) -> bytes: ... +def punycode_encode(text: str) -> bytes: ... +def decode_generalized_number(extended: bytes, extpos: int, bias: int, errors: str) -> tuple[int, int | None]: ... +def insertion_sort(base: str, extended: bytes, errors: str) -> str: ... +def punycode_decode(text: memoryview | bytes | bytearray | str, errors: str) -> str: ... + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: memoryview | bytes | bytearray | str, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: memoryview | bytes | bytearray | str, final: bool = False) -> str: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/quopri_codec.pyi b/mypy/typeshed/stdlib/encodings/quopri_codec.pyi new file mode 100644 index 0000000000000..e9deadd8d463c --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/quopri_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def quopri_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def quopri_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi b/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi new file mode 100644 index 0000000000000..74abb4623fab0 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/raw_unicode_escape.pyi @@ -0,0 +1,34 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.raw_unicode_escape_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.raw_unicode_escape_decode + if sys.version_info >= (3, 9): + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ... + else: + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +if sys.version_info >= (3, 9): + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ... + +else: + class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: str | ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... + +class StreamReader(Codec, codecs.StreamReader): + if sys.version_info >= (3, 9): + def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/rot_13.pyi b/mypy/typeshed/stdlib/encodings/rot_13.pyi new file mode 100644 index 0000000000000..8d71bc9575949 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/rot_13.pyi @@ -0,0 +1,23 @@ +import codecs +from _typeshed import SupportsRead, SupportsWrite + +# This codec is string to string. + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + def decode(self, input: str, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> str: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: str, final: bool = False) -> str: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +rot13_map: dict[int, int] + +def rot13(infile: SupportsRead[str], outfile: SupportsWrite[str]) -> None: ... diff --git a/mypy/typeshed/stdlib/encodings/shift_jis.pyi b/mypy/typeshed/stdlib/encodings/shift_jis.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/shift_jis.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/shift_jis_2004.pyi b/mypy/typeshed/stdlib/encodings/shift_jis_2004.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/shift_jis_2004.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/shift_jisx0213.pyi b/mypy/typeshed/stdlib/encodings/shift_jisx0213.pyi new file mode 100644 index 0000000000000..d613026a5a862 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/shift_jisx0213.pyi @@ -0,0 +1,23 @@ +import _multibytecodec as mbc +import codecs +from typing import ClassVar + +codec: mbc._MultibyteCodec + +class Codec(codecs.Codec): + encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType] + +class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder): + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc] + codec: ClassVar[mbc._MultibyteCodec] = ... + +class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter): + codec: ClassVar[mbc._MultibyteCodec] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/tis_620.pyi b/mypy/typeshed/stdlib/encodings/tis_620.pyi new file mode 100644 index 0000000000000..f62195662ce96 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/tis_620.pyi @@ -0,0 +1,21 @@ +import codecs +from _codecs import _EncodingMap +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: bytes, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... + +decoding_table: str +encoding_table: _EncodingMap diff --git a/mypy/typeshed/stdlib/encodings/undefined.pyi b/mypy/typeshed/stdlib/encodings/undefined.pyi new file mode 100644 index 0000000000000..4775dac752f28 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/undefined.pyi @@ -0,0 +1,20 @@ +import codecs +from _typeshed import ReadableBuffer + +# These return types are just to match the base types. In reality, these always +# raise an error. + +class Codec(codecs.Codec): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... +class StreamReader(Codec, codecs.StreamReader): ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/unicode_escape.pyi b/mypy/typeshed/stdlib/encodings/unicode_escape.pyi new file mode 100644 index 0000000000000..1e942f57916e7 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/unicode_escape.pyi @@ -0,0 +1,34 @@ +import codecs +import sys +from _typeshed import ReadableBuffer + +class Codec(codecs.Codec): + # At runtime, this is codecs.unicode_escape_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + # At runtime, this is codecs.unicode_escape_decode + if sys.version_info >= (3, 9): + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, final: bool = True, /) -> tuple[str, int]: ... + else: + @staticmethod + def decode(data: str | ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +if sys.version_info >= (3, 9): + class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: str | ReadableBuffer, errors: str | None, final: bool) -> tuple[str, int]: ... + +else: + class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: str | ReadableBuffer, final: bool = False) -> str: ... + +class StreamWriter(Codec, codecs.StreamWriter): ... + +class StreamReader(Codec, codecs.StreamReader): + if sys.version_info >= (3, 9): + def decode(self, input: str | ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override] + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_16.pyi b/mypy/typeshed/stdlib/encodings/utf_16.pyi new file mode 100644 index 0000000000000..3b712cde420ae --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_16.pyi @@ -0,0 +1,20 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_16_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: ReadableBuffer, errors: str, final: bool) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_16_be.pyi b/mypy/typeshed/stdlib/encodings/utf_16_be.pyi new file mode 100644 index 0000000000000..cc7d1534fc69b --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_16_be.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_16_be_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_16_be_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_16_be_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_16_be_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_16_le.pyi b/mypy/typeshed/stdlib/encodings/utf_16_le.pyi new file mode 100644 index 0000000000000..ba103eb088e3c --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_16_le.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_16_le_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_16_le_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_16_le_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_16_le_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_32.pyi b/mypy/typeshed/stdlib/encodings/utf_32.pyi new file mode 100644 index 0000000000000..c925be712c728 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_32.pyi @@ -0,0 +1,20 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_32_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, input: ReadableBuffer, errors: str, final: bool) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_32_be.pyi b/mypy/typeshed/stdlib/encodings/utf_32_be.pyi new file mode 100644 index 0000000000000..9d28f5199c501 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_32_be.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_32_be_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_32_be_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_32_be_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_32_be_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_32_le.pyi b/mypy/typeshed/stdlib/encodings/utf_32_le.pyi new file mode 100644 index 0000000000000..5be14a91a3e6c --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_32_le.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_32_le_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_32_le_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_32_le_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_32_le_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_7.pyi b/mypy/typeshed/stdlib/encodings/utf_7.pyi new file mode 100644 index 0000000000000..dc1162f34c287 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/utf_7.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer + +encode = codecs.utf_7_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: str, final: bool = False) -> bytes: ... + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_7_decode + @staticmethod + def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_7_encode + @staticmethod + def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... + +class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_7_decode + @staticmethod + def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/utf_8.pyi b/mypy/typeshed/stdlib/encodings/utf_8.pyi index bb745399eb8cb..918712d804730 100644 --- a/mypy/typeshed/stdlib/encodings/utf_8.pyi +++ b/mypy/typeshed/stdlib/encodings/utf_8.pyi @@ -1,21 +1,26 @@ import codecs from _typeshed import ReadableBuffer +encode = codecs.utf_8_encode + +def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... + class IncrementalEncoder(codecs.IncrementalEncoder): def encode(self, input: str, final: bool = False) -> bytes: ... class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + # At runtime, this is codecs.utf_8_decode @staticmethod def _buffer_decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... class StreamWriter(codecs.StreamWriter): + # At runtime, this is codecs.utf_8_encode @staticmethod def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... class StreamReader(codecs.StreamReader): + # At runtime, this is codecs.utf_8_decode @staticmethod def decode(data: ReadableBuffer, errors: str | None = None, final: bool = False, /) -> tuple[str, int]: ... def getregentry() -> codecs.CodecInfo: ... -def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... -def decode(input: ReadableBuffer, errors: str | None = "strict") -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/encodings/uu_codec.pyi b/mypy/typeshed/stdlib/encodings/uu_codec.pyi new file mode 100644 index 0000000000000..e32ba8ac0a1a7 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/uu_codec.pyi @@ -0,0 +1,28 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def uu_encode( + input: ReadableBuffer, errors: str = "strict", filename: str = "", mode: int = 0o666 +) -> tuple[bytes, int]: ... +def uu_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/encodings/zlib_codec.pyi b/mypy/typeshed/stdlib/encodings/zlib_codec.pyi new file mode 100644 index 0000000000000..0f13d0e810e91 --- /dev/null +++ b/mypy/typeshed/stdlib/encodings/zlib_codec.pyi @@ -0,0 +1,26 @@ +import codecs +from _typeshed import ReadableBuffer +from typing import ClassVar + +# This codec is bytes to bytes. + +def zlib_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... +def zlib_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... + +class Codec(codecs.Codec): + def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override] + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override] + +class StreamWriter(Codec, codecs.StreamWriter): + charbuffertype: ClassVar[type] = ... + +class StreamReader(Codec, codecs.StreamReader): + charbuffertype: ClassVar[type] = ... + +def getregentry() -> codecs.CodecInfo: ... diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index 5c82b07c41852..3b6c325522d7b 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -316,13 +316,24 @@ else: __rand__ = __and__ __rxor__ = __xor__ -# subclassing IntFlag so it picks up all implemented base functions, best modeling behavior of enum.auto() -class auto(IntFlag): +class auto: _value_: Any @_magic_enum_attr def value(self) -> Any: ... def __new__(cls) -> Self: ... + # These don't exist, but auto is basically immediately replaced with + # either an int or a str depending on the type of the enum. StrEnum's auto + # shouldn't have these, but they're needed for int versions of auto (mostly the __or__). + # Ideally type checkers would special case auto enough to handle this, + # but until then this is a slightly inaccurate helping hand. + def __or__(self, other: int | Self) -> Self: ... + def __and__(self, other: int | Self) -> Self: ... + def __xor__(self, other: int | Self) -> Self: ... + __ror__ = __or__ + __rand__ = __and__ + __rxor__ = __xor__ + if sys.version_info >= (3, 11): def pickle_by_global_name(self: Enum, proto: int) -> str: ... def pickle_by_enum_name(self: _EnumMemberT, proto: int) -> tuple[Callable[..., Any], tuple[type[_EnumMemberT], str]]: ... diff --git a/mypy/typeshed/stdlib/fcntl.pyi b/mypy/typeshed/stdlib/fcntl.pyi index ccf638205bbe2..71078b3b4579a 100644 --- a/mypy/typeshed/stdlib/fcntl.pyi +++ b/mypy/typeshed/stdlib/fcntl.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import FileDescriptorLike, ReadOnlyBuffer, WriteableBuffer -from typing import Any, Literal, overload +from typing import Any, Final, Literal, overload from typing_extensions import Buffer if sys.platform != "win32": @@ -44,9 +44,10 @@ if sys.platform != "win32": F_SEAL_SHRINK: int F_SEAL_WRITE: int if sys.version_info >= (3, 9): - F_OFD_GETLK: int - F_OFD_SETLK: int - F_OFD_SETLKW: int + F_OFD_GETLK: Final[int] + F_OFD_SETLK: Final[int] + F_OFD_SETLKW: Final[int] + if sys.version_info >= (3, 10): F_GETPIPE_SZ: int F_SETPIPE_SZ: int @@ -69,8 +70,10 @@ if sys.platform != "win32": LOCK_RW: int LOCK_WRITE: int - # These are highly problematic, they might be present or not, depends on the specific OS. if sys.platform == "linux": + # Constants for the POSIX STREAMS interface. Present in glibc until 2.29 (released February 2019). + # Never implemented on BSD, and considered "obsolescent" starting in POSIX 2008. + # Probably still used on Solaris. I_ATMARK: int I_CANPUT: int I_CKBAND: int @@ -105,6 +108,36 @@ if sys.platform != "win32": FICLONE: int FICLONERANGE: int + if sys.version_info >= (3, 13) and sys.platform == "linux": + F_OWNER_TID: Final = 0 + F_OWNER_PID: Final = 1 + F_OWNER_PGRP: Final = 2 + F_SETOWN_EX: Final = 15 + F_GETOWN_EX: Final = 16 + F_SEAL_FUTURE_WRITE: Final = 16 + F_GET_RW_HINT: Final = 1035 + F_SET_RW_HINT: Final = 1036 + F_GET_FILE_RW_HINT: Final = 1037 + F_SET_FILE_RW_HINT: Final = 1038 + RWH_WRITE_LIFE_NOT_SET: Final = 0 + RWH_WRITE_LIFE_NONE: Final = 1 + RWH_WRITE_LIFE_SHORT: Final = 2 + RWH_WRITE_LIFE_MEDIUM: Final = 3 + RWH_WRITE_LIFE_LONG: Final = 4 + RWH_WRITE_LIFE_EXTREME: Final = 5 + + if sys.version_info >= (3, 11) and sys.platform == "darwin": + F_OFD_SETLK: Final = 90 + F_OFD_SETLKW: Final = 91 + F_OFD_GETLK: Final = 92 + + if sys.version_info >= (3, 13) and sys.platform != "linux": + # OSx and NetBSD + F_GETNOSIGPIPE: Final[int] + F_SETNOSIGPIPE: Final[int] + # OSx and FreeBSD + F_RDAHEAD: Final[int] + @overload def fcntl(fd: FileDescriptorLike, cmd: int, arg: int = 0, /) -> int: ... @overload diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi index 5c8232d800d5f..cb7b945960773 100644 --- a/mypy/typeshed/stdlib/filecmp.pyi +++ b/mypy/typeshed/stdlib/filecmp.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import GenericPath, StrOrBytesPath from collections.abc import Callable, Iterable, Sequence -from typing import Any, AnyStr, Generic, Literal +from typing import Any, AnyStr, Final, Generic, Literal if sys.version_info >= (3, 9): from types import GenericAlias @@ -9,7 +9,7 @@ if sys.version_info >= (3, 9): __all__ = ["clear_cache", "cmp", "dircmp", "cmpfiles", "DEFAULT_IGNORES"] DEFAULT_IGNORES: list[str] -BUFSIZE: Literal[8192] +BUFSIZE: Final = 8192 def cmp(f1: StrOrBytesPath, f2: StrOrBytesPath, shallow: bool | Literal[0, 1] = True) -> bool: ... def cmpfiles( @@ -17,13 +17,24 @@ def cmpfiles( ) -> tuple[list[AnyStr], list[AnyStr], list[AnyStr]]: ... class dircmp(Generic[AnyStr]): - def __init__( - self, - a: GenericPath[AnyStr], - b: GenericPath[AnyStr], - ignore: Sequence[AnyStr] | None = None, - hide: Sequence[AnyStr] | None = None, - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + a: GenericPath[AnyStr], + b: GenericPath[AnyStr], + ignore: Sequence[AnyStr] | None = None, + hide: Sequence[AnyStr] | None = None, + *, + shallow: bool = True, + ) -> None: ... + else: + def __init__( + self, + a: GenericPath[AnyStr], + b: GenericPath[AnyStr], + ignore: Sequence[AnyStr] | None = None, + hide: Sequence[AnyStr] | None = None, + ) -> None: ... left: AnyStr right: AnyStr hide: Sequence[AnyStr] diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index 086aff50344c7..fbcfa868cc1bb 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -2,7 +2,7 @@ import sys from collections.abc import Callable from decimal import Decimal from numbers import Integral, Rational, Real -from typing import Any, Literal, SupportsIndex, overload +from typing import Any, Literal, Protocol, SupportsIndex, overload from typing_extensions import Self, TypeAlias _ComparableNum: TypeAlias = int | float | Decimal | Real @@ -20,11 +20,19 @@ else: @overload def gcd(a: Integral, b: Integral) -> Integral: ... +class _ConvertibleToIntegerRatio(Protocol): + def as_integer_ratio(self) -> tuple[int | Rational, int | Rational]: ... + class Fraction(Rational): @overload def __new__(cls, numerator: int | Rational = 0, denominator: int | Rational | None = None) -> Self: ... @overload def __new__(cls, value: float | Decimal | str, /) -> Self: ... + + if sys.version_info >= (3, 14): + @overload + def __new__(cls, value: _ConvertibleToIntegerRatio) -> Self: ... + @classmethod def from_float(cls, f: float) -> Self: ... @classmethod diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 9e7097ddc56e8..3693d7c52a26c 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -4,16 +4,16 @@ from collections.abc import Callable, Iterable, Iterator from socket import socket from ssl import SSLContext from types import TracebackType -from typing import Any, Literal, TextIO +from typing import Any, Final, Literal, TextIO from typing_extensions import Self __all__ = ["FTP", "error_reply", "error_temp", "error_perm", "error_proto", "all_errors", "FTP_TLS"] -MSG_OOB: Literal[1] -FTP_PORT: Literal[21] -MAXLINE: Literal[8192] -CRLF: Literal["\r\n"] -B_CRLF: Literal[b"\r\n"] +MSG_OOB: Final = 1 +FTP_PORT: Final = 21 +MAXLINE: Final = 8192 +CRLF: Final = "\r\n" +B_CRLF: Final = b"\r\n" class Error(Exception): ... class error_reply(Error): ... @@ -86,7 +86,7 @@ class FTP: def makeport(self) -> socket: ... def makepasv(self) -> tuple[str, int]: ... def login(self, user: str = "", passwd: str = "", acct: str = "") -> str: ... - # In practice, `rest` rest can actually be anything whose str() is an integer sequence, so to make it simple we allow integers. + # In practice, `rest` can actually be anything whose str() is an integer sequence, so to make it simple we allow integers def ntransfercmd(self, cmd: str, rest: int | str | None = None) -> tuple[socket, int | None]: ... def transfercmd(self, cmd: str, rest: int | str | None = None) -> socket: ... def retrbinary( diff --git a/mypy/typeshed/stdlib/gc.pyi b/mypy/typeshed/stdlib/gc.pyi index 31179add314cf..9d34e0d6213a3 100644 --- a/mypy/typeshed/stdlib/gc.pyi +++ b/mypy/typeshed/stdlib/gc.pyi @@ -1,13 +1,13 @@ import sys from collections.abc import Callable -from typing import Any, Literal +from typing import Any, Final, Literal from typing_extensions import TypeAlias -DEBUG_COLLECTABLE: Literal[2] -DEBUG_LEAK: Literal[38] -DEBUG_SAVEALL: Literal[32] -DEBUG_STATS: Literal[1] -DEBUG_UNCOLLECTABLE: Literal[4] +DEBUG_COLLECTABLE: Final = 2 +DEBUG_LEAK: Final = 38 +DEBUG_SAVEALL: Final = 32 +DEBUG_STATS: Final = 1 +DEBUG_UNCOLLECTABLE: Final = 4 _CallbackType: TypeAlias = Callable[[Literal["start", "stop"], dict[str, int]], object] @@ -34,4 +34,4 @@ if sys.version_info >= (3, 9): def isenabled() -> bool: ... def set_debug(flags: int, /) -> None: ... -def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ...) -> None: ... +def set_threshold(threshold0: int, threshold1: int = ..., threshold2: int = ..., /) -> None: ... diff --git a/mypy/typeshed/stdlib/getopt.pyi b/mypy/typeshed/stdlib/getopt.pyi index bc9d4da4796b2..bcc8d9750b199 100644 --- a/mypy/typeshed/stdlib/getopt.pyi +++ b/mypy/typeshed/stdlib/getopt.pyi @@ -1,7 +1,11 @@ +from collections.abc import Iterable + __all__ = ["GetoptError", "error", "getopt", "gnu_getopt"] -def getopt(args: list[str], shortopts: str, longopts: list[str] = []) -> tuple[list[tuple[str, str]], list[str]]: ... -def gnu_getopt(args: list[str], shortopts: str, longopts: list[str] = []) -> tuple[list[tuple[str, str]], list[str]]: ... +def getopt(args: list[str], shortopts: str, longopts: Iterable[str] | str = []) -> tuple[list[tuple[str, str]], list[str]]: ... +def gnu_getopt( + args: list[str], shortopts: str, longopts: Iterable[str] | str = [] +) -> tuple[list[tuple[str, str]], list[str]]: ... class GetoptError(Exception): msg: str diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi index 4c17c0dc5de42..d8fd92a00e132 100644 --- a/mypy/typeshed/stdlib/gettext.pyi +++ b/mypy/typeshed/stdlib/gettext.pyi @@ -167,3 +167,5 @@ if sys.version_info < (3, 11): def bind_textdomain_codeset(domain: str, codeset: str | None = None) -> str: ... Catalog = translation + +def c2py(plural: str) -> Callable[[int], int]: ... diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 542945698bba0..9b32008dcbf65 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -3,7 +3,7 @@ import sys import zlib from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath from io import FileIO -from typing import Literal, Protocol, TextIO, overload +from typing import Final, Literal, Protocol, TextIO, overload from typing_extensions import TypeAlias __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] @@ -12,14 +12,14 @@ _ReadBinaryMode: TypeAlias = Literal["r", "rb"] _WriteBinaryMode: TypeAlias = Literal["a", "ab", "w", "wb", "x", "xb"] _OpenTextMode: TypeAlias = Literal["rt", "at", "wt", "xt"] -READ: object # undocumented -WRITE: object # undocumented +READ: Final[object] # undocumented +WRITE: Final[object] # undocumented -FTEXT: int # actually Literal[1] # undocumented -FHCRC: int # actually Literal[2] # undocumented -FEXTRA: int # actually Literal[4] # undocumented -FNAME: int # actually Literal[8] # undocumented -FCOMMENT: int # actually Literal[16] # undocumented +FTEXT: Final[int] # actually Literal[1] # undocumented +FHCRC: Final[int] # actually Literal[2] # undocumented +FEXTRA: Final[int] # actually Literal[4] # undocumented +FNAME: Final[int] # actually Literal[8] # undocumented +FCOMMENT: Final[int] # actually Literal[16] # undocumented class _ReadableFileobj(Protocol): def read(self, n: int, /) -> bytes: ... diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index 93bd986c9d314..db6f8635054d0 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -1,8 +1,19 @@ import sys +from _blake2 import blake2b as blake2b, blake2s as blake2s +from _hashlib import ( + HASH, + openssl_md5 as md5, + openssl_sha1 as sha1, + openssl_sha224 as sha224, + openssl_sha256 as sha256, + openssl_sha384 as sha384, + openssl_sha512 as sha512, + pbkdf2_hmac as pbkdf2_hmac, + scrypt as scrypt, +) from _typeshed import ReadableBuffer from collections.abc import Callable, Set as AbstractSet -from typing import Protocol, final -from typing_extensions import Self +from typing import Protocol, type_check_only if sys.version_info >= (3, 11): __all__ = ( @@ -48,112 +59,35 @@ else: "pbkdf2_hmac", ) -class _Hash: - @property - def digest_size(self) -> int: ... - @property - def block_size(self) -> int: ... - @property - def name(self) -> str: ... - def __init__(self, data: ReadableBuffer = ...) -> None: ... - def copy(self) -> Self: ... - def digest(self) -> bytes: ... - def hexdigest(self) -> str: ... - def update(self, data: ReadableBuffer, /) -> None: ... - if sys.version_info >= (3, 9): - def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> _Hash: ... - def md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... - def sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> _Hash: ... + def new(name: str, data: ReadableBuffer = b"", *, usedforsecurity: bool = ...) -> HASH: ... + from _hashlib import ( + openssl_sha3_224 as sha3_224, + openssl_sha3_256 as sha3_256, + openssl_sha3_384 as sha3_384, + openssl_sha3_512 as sha3_512, + openssl_shake_128 as shake_128, + openssl_shake_256 as shake_256, + ) else: - def new(name: str, data: ReadableBuffer = b"") -> _Hash: ... - def md5(string: ReadableBuffer = b"") -> _Hash: ... - def sha1(string: ReadableBuffer = b"") -> _Hash: ... - def sha224(string: ReadableBuffer = b"") -> _Hash: ... - def sha256(string: ReadableBuffer = b"") -> _Hash: ... - def sha384(string: ReadableBuffer = b"") -> _Hash: ... - def sha512(string: ReadableBuffer = b"") -> _Hash: ... + @type_check_only + class _VarLenHash(HASH): + def digest(self, length: int) -> bytes: ... # type: ignore[override] + def hexdigest(self, length: int) -> str: ... # type: ignore[override] + + def new(name: str, data: ReadableBuffer = b"") -> HASH: ... + # At runtime these aren't functions but classes imported from _sha3 + def sha3_224(string: ReadableBuffer = b"") -> HASH: ... + def sha3_256(string: ReadableBuffer = b"") -> HASH: ... + def sha3_384(string: ReadableBuffer = b"") -> HASH: ... + def sha3_512(string: ReadableBuffer = b"") -> HASH: ... + def shake_128(string: ReadableBuffer = b"") -> _VarLenHash: ... + def shake_256(string: ReadableBuffer = b"") -> _VarLenHash: ... algorithms_guaranteed: AbstractSet[str] algorithms_available: AbstractSet[str] -def pbkdf2_hmac( - hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None -) -> bytes: ... - -class _VarLenHash: - digest_size: int - block_size: int - name: str - def __init__(self, data: ReadableBuffer = ...) -> None: ... - def copy(self) -> _VarLenHash: ... - def digest(self, length: int, /) -> bytes: ... - def hexdigest(self, length: int, /) -> str: ... - def update(self, data: ReadableBuffer, /) -> None: ... - -sha3_224 = _Hash -sha3_256 = _Hash -sha3_384 = _Hash -sha3_512 = _Hash -shake_128 = _VarLenHash -shake_256 = _VarLenHash - -def scrypt( - password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64 -) -> bytes: ... -@final -class _BlakeHash(_Hash): - MAX_DIGEST_SIZE: int - MAX_KEY_SIZE: int - PERSON_SIZE: int - SALT_SIZE: int - - if sys.version_info >= (3, 9): - def __init__( - self, - data: ReadableBuffer = ..., - /, - *, - digest_size: int = ..., - key: ReadableBuffer = ..., - salt: ReadableBuffer = ..., - person: ReadableBuffer = ..., - fanout: int = ..., - depth: int = ..., - leaf_size: int = ..., - node_offset: int = ..., - node_depth: int = ..., - inner_size: int = ..., - last_node: bool = ..., - usedforsecurity: bool = ..., - ) -> None: ... - else: - def __init__( - self, - data: ReadableBuffer = ..., - /, - *, - digest_size: int = ..., - key: ReadableBuffer = ..., - salt: ReadableBuffer = ..., - person: ReadableBuffer = ..., - fanout: int = ..., - depth: int = ..., - leaf_size: int = ..., - node_offset: int = ..., - node_depth: int = ..., - inner_size: int = ..., - last_node: bool = ..., - ) -> None: ... - -blake2b = _BlakeHash -blake2s = _BlakeHash - if sys.version_info >= (3, 11): class _BytesIOLike(Protocol): def getbuffer(self) -> ReadableBuffer: ... @@ -163,5 +97,8 @@ if sys.version_info >= (3, 11): def readable(self) -> bool: ... def file_digest( - fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], _Hash], /, *, _bufsize: int = 262144 - ) -> _Hash: ... + fileobj: _BytesIOLike | _FileDigestFileObj, digest: str | Callable[[], HASH], /, *, _bufsize: int = 262144 + ) -> HASH: ... + +# Legacy typing-only alias +_Hash = HASH diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index ac1372dd1e9c2..efd649ec39a88 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -1,12 +1,12 @@ +import sys +from _hashlib import HASH as _HashlibHash from _typeshed import ReadableBuffer, SizedBuffer from collections.abc import Callable from types import ModuleType -from typing import Any, AnyStr, overload +from typing import AnyStr, overload from typing_extensions import TypeAlias -# TODO more precise type for object of hashlib -_Hash: TypeAlias = Any -_DigestMod: TypeAlias = str | Callable[[], _Hash] | ModuleType +_DigestMod: TypeAlias = str | Callable[[], _HashlibHash] | ModuleType trans_5C: bytes trans_36: bytes @@ -31,8 +31,12 @@ class HMAC: def hexdigest(self) -> str: ... def copy(self) -> HMAC: ... -@overload -def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ... -@overload -def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... def digest(key: SizedBuffer, msg: ReadableBuffer, digest: _DigestMod) -> bytes: ... + +if sys.version_info >= (3, 9): + from _hashlib import compare_digest as compare_digest +else: + @overload + def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ... + @overload + def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index f68d9d0ca7d74..3db764ef1e7cf 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -3,10 +3,10 @@ import io import ssl import sys import types -from _typeshed import ReadableBuffer, SupportsRead, SupportsReadline, WriteableBuffer +from _typeshed import MaybeNone, ReadableBuffer, SupportsRead, SupportsReadline, WriteableBuffer from collections.abc import Callable, Iterable, Iterator, Mapping from socket import socket -from typing import Any, BinaryIO, TypeVar, overload +from typing import BinaryIO, TypeVar, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -34,6 +34,7 @@ __all__ = [ _DataType: TypeAlias = SupportsRead[bytes] | Iterable[ReadableBuffer] | ReadableBuffer _T = TypeVar("_T") _MessageT = TypeVar("_MessageT", bound=email.message.Message) +_HeaderValue: TypeAlias = ReadableBuffer | str | int HTTP_PORT: int HTTPS_PORT: int @@ -153,7 +154,7 @@ class HTTPConnection: timeout: float | None host: str port: int - sock: socket | Any # can be `None` if `.connect()` was not called + sock: socket | MaybeNone # can be `None` if `.connect()` was not called def __init__( self, host: str, @@ -167,7 +168,7 @@ class HTTPConnection: method: str, url: str, body: _DataType | str | None = None, - headers: Mapping[str, str] = {}, + headers: Mapping[str, _HeaderValue] = {}, *, encode_chunked: bool = False, ) -> None: ... @@ -180,13 +181,13 @@ class HTTPConnection: def connect(self) -> None: ... def close(self) -> None: ... def putrequest(self, method: str, url: str, skip_host: bool = False, skip_accept_encoding: bool = False) -> None: ... - def putheader(self, header: str | bytes, *argument: str | bytes) -> None: ... + def putheader(self, header: str | bytes, *values: _HeaderValue) -> None: ... def endheaders(self, message_body: _DataType | None = None, *, encode_chunked: bool = False) -> None: ... def send(self, data: _DataType | str) -> None: ... class HTTPSConnection(HTTPConnection): # Can be `None` if `.connect()` was not called: - sock: ssl.SSLSocket | Any + sock: ssl.SSLSocket | MaybeNone if sys.version_info >= (3, 12): def __init__( self, diff --git a/mypy/typeshed/stdlib/http/cookiejar.pyi b/mypy/typeshed/stdlib/http/cookiejar.pyi index faac20d13125a..31e1d3fc83785 100644 --- a/mypy/typeshed/stdlib/http/cookiejar.pyi +++ b/mypy/typeshed/stdlib/http/cookiejar.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import StrPath -from collections.abc import Iterable, Iterator, Sequence +from collections.abc import Iterator, Sequence from http.client import HTTPResponse from re import Pattern from typing import ClassVar, TypeVar, overload @@ -21,7 +21,7 @@ _T = TypeVar("_T") class LoadError(OSError): ... -class CookieJar(Iterable[Cookie]): +class CookieJar: non_word_re: ClassVar[Pattern[str]] # undocumented quote_re: ClassVar[Pattern[str]] # undocumented strict_domain_re: ClassVar[Pattern[str]] # undocumented @@ -42,7 +42,7 @@ class CookieJar(Iterable[Cookie]): def __len__(self) -> int: ... class FileCookieJar(CookieJar): - filename: str + filename: str | None delayload: bool def __init__(self, filename: StrPath | None = None, delayload: bool = False, policy: CookiePolicy | None = None) -> None: ... def save(self, filename: str | None = None, ignore_discard: bool = False, ignore_expires: bool = False) -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/__init__.pyi b/mypy/typeshed/stdlib/importlib/__init__.pyi index 8506efc01171f..cab81512e92f2 100644 --- a/mypy/typeshed/stdlib/importlib/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/__init__.pyi @@ -1,19 +1,10 @@ import sys -from collections.abc import Mapping, Sequence +from importlib._bootstrap import __import__ as __import__ from importlib.abc import Loader from types import ModuleType __all__ = ["__import__", "import_module", "invalidate_caches", "reload"] -# Signature of `builtins.__import__` should be kept identical to `importlib.__import__` -def __import__( - name: str, - globals: Mapping[str, object] | None = None, - locals: Mapping[str, object] | None = None, - fromlist: Sequence[str] = (), - level: int = 0, -) -> ModuleType: ... - # `importlib.import_module` return type should be kept the same as `builtins.__import__` def import_module(name: str, package: str | None = None) -> ModuleType: ... diff --git a/mypy/typeshed/stdlib/importlib/_bootstrap.pyi b/mypy/typeshed/stdlib/importlib/_bootstrap.pyi new file mode 100644 index 0000000000000..02427ff420620 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/_bootstrap.pyi @@ -0,0 +1,2 @@ +from _frozen_importlib import * +from _frozen_importlib import __import__ as __import__, _init_module_attrs as _init_module_attrs diff --git a/mypy/typeshed/stdlib/importlib/_bootstrap_external.pyi b/mypy/typeshed/stdlib/importlib/_bootstrap_external.pyi new file mode 100644 index 0000000000000..6210ce7083afa --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/_bootstrap_external.pyi @@ -0,0 +1,2 @@ +from _frozen_importlib_external import * +from _frozen_importlib_external import _NamespaceLoader as _NamespaceLoader diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 3937481159dce..588377d7d8718 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -4,6 +4,7 @@ import types from _typeshed import ReadableBuffer, StrPath from abc import ABCMeta, abstractmethod from collections.abc import Iterator, Mapping, Sequence +from importlib import _bootstrap_external from importlib.machinery import ModuleSpec from io import BufferedReader from typing import IO, Any, Literal, Protocol, overload, runtime_checkable @@ -56,7 +57,7 @@ class ExecutionLoader(InspectLoader): @abstractmethod def get_filename(self, fullname: str) -> str: ... -class SourceLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): +class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader, metaclass=ABCMeta): # type: ignore[misc] # incompatible definitions of source_to_code in the base classes def path_mtime(self, path: str) -> float: ... def set_data(self, path: str, data: bytes) -> None: ... def get_source(self, fullname: str) -> str | None: ... @@ -101,7 +102,7 @@ else: # Not defined on the actual class, but expected to exist. def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ... -class FileLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta): +class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader, metaclass=ABCMeta): name: str path: str def __init__(self, fullname: str, path: str) -> None: ... @@ -145,10 +146,10 @@ if sys.version_info >= (3, 9): # which is not the case. @overload @abstractmethod - def open(self, mode: Literal["r"] = "r", /, *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... + def open(self, mode: Literal["r"] = "r", *, encoding: str | None = None, errors: str | None = None) -> IO[str]: ... @overload @abstractmethod - def open(self, mode: Literal["rb"], /) -> IO[bytes]: ... + def open(self, mode: Literal["rb"]) -> IO[bytes]: ... @property @abstractmethod def name(self) -> str: ... diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index 586c2b80ab7bd..bb1a6f93d0e09 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -1,179 +1,20 @@ -import importlib.abc import sys -import types -from _typeshed import ReadableBuffer -from collections.abc import Callable, Iterable, MutableSequence, Sequence -from importlib.metadata import DistributionFinder, PathDistribution -from typing import Any, Literal -from typing_extensions import deprecated - -class ModuleSpec: - def __init__( - self, - name: str, - loader: importlib.abc.Loader | None, - *, - origin: str | None = None, - loader_state: Any = None, - is_package: bool | None = None, - ) -> None: ... - name: str - loader: importlib.abc.Loader | None - origin: str | None - submodule_search_locations: list[str] | None - loader_state: Any - cached: str | None - @property - def parent(self) -> str | None: ... - has_location: bool - def __eq__(self, other: object) -> bool: ... - -class BuiltinImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): - # MetaPathFinder - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - # InspectLoader - @classmethod - def is_package(cls, fullname: str) -> bool: ... - @classmethod - def load_module(cls, fullname: str) -> types.ModuleType: ... - @classmethod - def get_code(cls, fullname: str) -> None: ... - @classmethod - def get_source(cls, fullname: str) -> None: ... - # Loader - if sys.version_info < (3, 12): - @staticmethod - def module_repr(module: types.ModuleType) -> str: ... - if sys.version_info >= (3, 10): - @staticmethod - def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... - @staticmethod - def exec_module(module: types.ModuleType) -> None: ... - else: - @classmethod - def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... - @classmethod - def exec_module(cls, module: types.ModuleType) -> None: ... - -class FrozenImporter(importlib.abc.MetaPathFinder, importlib.abc.InspectLoader): - # MetaPathFinder - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - # InspectLoader - @classmethod - def is_package(cls, fullname: str) -> bool: ... - @classmethod - def load_module(cls, fullname: str) -> types.ModuleType: ... - @classmethod - def get_code(cls, fullname: str) -> None: ... - @classmethod - def get_source(cls, fullname: str) -> None: ... - # Loader - if sys.version_info < (3, 12): - @staticmethod - def module_repr(m: types.ModuleType) -> str: ... - if sys.version_info >= (3, 10): - @staticmethod - def create_module(spec: ModuleSpec) -> types.ModuleType | None: ... - else: - @classmethod - def create_module(cls, spec: ModuleSpec) -> types.ModuleType | None: ... - - @staticmethod - def exec_module(module: types.ModuleType) -> None: ... - -class WindowsRegistryFinder(importlib.abc.MetaPathFinder): - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - -class PathFinder: - if sys.version_info >= (3, 10): - @staticmethod - def invalidate_caches() -> None: ... - else: - @classmethod - def invalidate_caches(cls) -> None: ... - if sys.version_info >= (3, 10): - @staticmethod - def find_distributions(context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... - else: - @classmethod - def find_distributions(cls, context: DistributionFinder.Context = ...) -> Iterable[PathDistribution]: ... - - @classmethod - def find_spec( - cls, fullname: str, path: Sequence[str] | None = None, target: types.ModuleType | None = None - ) -> ModuleSpec | None: ... - if sys.version_info < (3, 12): - @classmethod - def find_module(cls, fullname: str, path: Sequence[str] | None = None) -> importlib.abc.Loader | None: ... - -SOURCE_SUFFIXES: list[str] -DEBUG_BYTECODE_SUFFIXES: list[str] -OPTIMIZED_BYTECODE_SUFFIXES: list[str] -BYTECODE_SUFFIXES: list[str] -EXTENSION_SUFFIXES: list[str] - -def all_suffixes() -> list[str]: ... - -class FileFinder(importlib.abc.PathEntryFinder): - path: str - def __init__(self, path: str, *loader_details: tuple[type[importlib.abc.Loader], list[str]]) -> None: ... - @classmethod - def path_hook( - cls, *loader_details: tuple[type[importlib.abc.Loader], list[str]] - ) -> Callable[[str], importlib.abc.PathEntryFinder]: ... - -class SourceFileLoader(importlib.abc.FileLoader, importlib.abc.SourceLoader): - def set_data(self, path: str, data: ReadableBuffer, *, _mode: int = 0o666) -> None: ... - -class SourcelessFileLoader(importlib.abc.FileLoader, importlib.abc.SourceLoader): ... - -class ExtensionFileLoader(importlib.abc.ExecutionLoader): - def __init__(self, name: str, path: str) -> None: ... - def get_filename(self, name: str | None = None) -> str: ... - def get_source(self, fullname: str) -> None: ... - def create_module(self, spec: ModuleSpec) -> types.ModuleType: ... - def exec_module(self, module: types.ModuleType) -> None: ... - def get_code(self, fullname: str) -> None: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... +from importlib._bootstrap import BuiltinImporter as BuiltinImporter, FrozenImporter as FrozenImporter, ModuleSpec as ModuleSpec +from importlib._bootstrap_external import ( + BYTECODE_SUFFIXES as BYTECODE_SUFFIXES, + DEBUG_BYTECODE_SUFFIXES as DEBUG_BYTECODE_SUFFIXES, + EXTENSION_SUFFIXES as EXTENSION_SUFFIXES, + OPTIMIZED_BYTECODE_SUFFIXES as OPTIMIZED_BYTECODE_SUFFIXES, + SOURCE_SUFFIXES as SOURCE_SUFFIXES, + ExtensionFileLoader as ExtensionFileLoader, + FileFinder as FileFinder, + PathFinder as PathFinder, + SourceFileLoader as SourceFileLoader, + SourcelessFileLoader as SourcelessFileLoader, + WindowsRegistryFinder as WindowsRegistryFinder, +) if sys.version_info >= (3, 11): - import importlib.readers + from importlib._bootstrap_external import NamespaceLoader as NamespaceLoader - class NamespaceLoader(importlib.abc.InspectLoader): - def __init__( - self, name: str, path: MutableSequence[str], path_finder: Callable[[str, tuple[str, ...]], ModuleSpec] - ) -> None: ... - def is_package(self, fullname: str) -> Literal[True]: ... - def get_source(self, fullname: str) -> Literal[""]: ... - def get_code(self, fullname: str) -> types.CodeType: ... - def create_module(self, spec: ModuleSpec) -> None: ... - def exec_module(self, module: types.ModuleType) -> None: ... - @deprecated("load_module() is deprecated; use exec_module() instead") - def load_module(self, fullname: str) -> types.ModuleType: ... - def get_resource_reader(self, module: types.ModuleType) -> importlib.readers.NamespaceReader: ... - if sys.version_info < (3, 12): - @staticmethod - @deprecated("module_repr() is deprecated, and has been removed in Python 3.12") - def module_repr(module: types.ModuleType) -> str: ... +def all_suffixes() -> list[str]: ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 56ee205239508..5e26f89872772 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -1,6 +1,7 @@ import abc import pathlib import sys +import types from _collections_abc import dict_keys, dict_values from _typeshed import StrPath from collections.abc import Iterable, Iterator, Mapping @@ -36,11 +37,8 @@ if sys.version_info >= (3, 10): from importlib.metadata._meta import PackageMetadata as PackageMetadata, SimplePath def packages_distributions() -> Mapping[str, list[str]]: ... - if sys.version_info >= (3, 12): - # It's generic but shouldn't be - _SimplePath: TypeAlias = SimplePath[Any] - else: - _SimplePath: TypeAlias = SimplePath + _SimplePath: TypeAlias = SimplePath + else: _SimplePath: TypeAlias = Path @@ -48,7 +46,9 @@ class PackageNotFoundError(ModuleNotFoundError): @property def name(self) -> str: ... # type: ignore[override] -if sys.version_info >= (3, 11): +if sys.version_info >= (3, 13): + _EntryPointBase = object +elif sys.version_info >= (3, 11): class DeprecatedTuple: def __getitem__(self, item: int) -> str: ... @@ -155,7 +155,7 @@ if sys.version_info >= (3, 10) and sys.version_info < (3, 12): @property def names(self) -> set[str]: ... @overload - def select(self) -> Self: ... # type: ignore[misc] + def select(self) -> Self: ... @overload def select( self, @@ -226,6 +226,9 @@ class Distribution(_distribution_parent): if sys.version_info >= (3, 10): @property def name(self) -> str: ... + if sys.version_info >= (3, 13): + @property + def origin(self) -> types.SimpleNamespace: ... class DistributionFinder(MetaPathFinder): class Context: @@ -274,7 +277,7 @@ if sys.version_info >= (3, 12): elif sys.version_info >= (3, 10): @overload - def entry_points() -> SelectableGroups: ... # type: ignore[overload-overlap] + def entry_points() -> SelectableGroups: ... @overload def entry_points( *, name: str = ..., value: str = ..., group: str = ..., module: str = ..., attr: str = ..., extras: list[str] = ... diff --git a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi index 3eac226b7065e..9f791dab254fd 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi @@ -1,9 +1,12 @@ import sys +from _typeshed import StrPath from collections.abc import Iterator -from typing import Any, Protocol, TypeVar, overload +from os import PathLike +from typing import Any, Protocol, overload +from typing_extensions import TypeVar _T = TypeVar("_T") -_T_co = TypeVar("_T_co", covariant=True) +_T_co = TypeVar("_T_co", covariant=True, default=Any) class PackageMetadata(Protocol): def __len__(self) -> int: ... @@ -22,7 +25,18 @@ class PackageMetadata(Protocol): @overload def get(self, name: str, failobj: _T) -> _T | str: ... -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 13): + class SimplePath(Protocol): + def joinpath(self, other: StrPath, /) -> SimplePath: ... + def __truediv__(self, other: StrPath, /) -> SimplePath: ... + # Incorrect at runtime + @property + def parent(self) -> PathLike[str]: ... + def read_text(self, encoding: str | None = None) -> str: ... + def read_bytes(self) -> bytes: ... + def exists(self) -> bool: ... + +elif sys.version_info >= (3, 12): class SimplePath(Protocol[_T_co]): # At runtime this is defined as taking `str | _T`, but that causes trouble. # See #11436. diff --git a/mypy/typeshed/stdlib/importlib/metadata/diagnose.pyi b/mypy/typeshed/stdlib/importlib/metadata/diagnose.pyi new file mode 100644 index 0000000000000..565872fd976f5 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/metadata/diagnose.pyi @@ -0,0 +1,2 @@ +def inspect(path: str) -> None: ... +def run() -> None: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi index 8d656563772c3..f82df8c591fad 100644 --- a/mypy/typeshed/stdlib/importlib/resources/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/__init__.pyi @@ -7,10 +7,15 @@ from types import ModuleType from typing import Any, BinaryIO, TextIO from typing_extensions import TypeAlias +if sys.version_info >= (3, 11): + from importlib.resources._common import Package as Package +else: + Package: TypeAlias = str | ModuleType + if sys.version_info >= (3, 9): from importlib.abc import Traversable -__all__ = ["Package", "Resource", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"] +__all__ = ["Package", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"] if sys.version_info >= (3, 9): __all__ += ["as_file", "files"] @@ -18,26 +23,45 @@ if sys.version_info >= (3, 9): if sys.version_info >= (3, 10): __all__ += ["ResourceReader"] -Package: TypeAlias = str | ModuleType +if sys.version_info < (3, 13): + __all__ += ["Resource"] -if sys.version_info >= (3, 11): - Resource: TypeAlias = str -else: +if sys.version_info < (3, 11): Resource: TypeAlias = str | os.PathLike[Any] +elif sys.version_info < (3, 13): + Resource: TypeAlias = str -def open_binary(package: Package, resource: Resource) -> BinaryIO: ... -def open_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> TextIO: ... -def read_binary(package: Package, resource: Resource) -> bytes: ... -def read_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> str: ... -def path(package: Package, resource: Resource) -> AbstractContextManager[Path]: ... -def is_resource(package: Package, name: str) -> bool: ... -def contents(package: Package) -> Iterator[str]: ... +if sys.version_info >= (3, 13): + from importlib.resources._common import Anchor as Anchor -if sys.version_info >= (3, 9): + __all__ += ["Anchor"] + + from importlib.resources._functional import ( + contents as contents, + is_resource as is_resource, + open_binary as open_binary, + open_text as open_text, + path as path, + read_binary as read_binary, + read_text as read_text, + ) + +else: + def open_binary(package: Package, resource: Resource) -> BinaryIO: ... + def open_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> TextIO: ... + def read_binary(package: Package, resource: Resource) -> bytes: ... + def read_text(package: Package, resource: Resource, encoding: str = "utf-8", errors: str = "strict") -> str: ... + def path(package: Package, resource: Resource) -> AbstractContextManager[Path]: ... + def is_resource(package: Package, name: str) -> bool: ... + def contents(package: Package) -> Iterator[str]: ... + +if sys.version_info >= (3, 11): + from importlib.resources._common import as_file as as_file +elif sys.version_info >= (3, 9): def as_file(path: Traversable) -> AbstractContextManager[Path]: ... -if sys.version_info >= (3, 12): - def files(anchor: Package | None = ...) -> Traversable: ... +if sys.version_info >= (3, 11): + from importlib.resources._common import files as files elif sys.version_info >= (3, 9): def files(package: Package) -> Traversable: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/_common.pyi b/mypy/typeshed/stdlib/importlib/resources/_common.pyi new file mode 100644 index 0000000000000..f04f70f25e230 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/resources/_common.pyi @@ -0,0 +1,42 @@ +import sys + +# Even though this file is 3.11+ only, Pyright will complain in stubtest for older versions. +if sys.version_info >= (3, 11): + import types + from collections.abc import Callable + from contextlib import AbstractContextManager + from importlib.abc import ResourceReader, Traversable + from pathlib import Path + from typing import overload + from typing_extensions import TypeAlias, deprecated + + Package: TypeAlias = str | types.ModuleType + + if sys.version_info >= (3, 12): + Anchor: TypeAlias = Package + + def package_to_anchor( + func: Callable[[Anchor | None], Traversable] + ) -> Callable[[Anchor | None, Anchor | None], Traversable]: ... + @overload + def files(anchor: Anchor | None = None) -> Traversable: ... + @overload + @deprecated("First parameter to files is renamed to 'anchor'") + def files(package: Anchor | None = None) -> Traversable: ... + + else: + def files(package: Package) -> Traversable: ... + + def get_resource_reader(package: types.ModuleType) -> ResourceReader | None: ... + + if sys.version_info >= (3, 12): + def resolve(cand: Anchor | None) -> types.ModuleType: ... + + else: + def resolve(cand: Package) -> types.ModuleType: ... + + if sys.version_info < (3, 12): + def get_package(package: Package) -> types.ModuleType: ... + + def from_package(package: types.ModuleType) -> Traversable: ... + def as_file(path: Traversable) -> AbstractContextManager[Path]: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/_functional.pyi b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi new file mode 100644 index 0000000000000..97e46bdf0a536 --- /dev/null +++ b/mypy/typeshed/stdlib/importlib/resources/_functional.pyi @@ -0,0 +1,30 @@ +import sys + +# Even though this file is 3.13+ only, Pyright will complain in stubtest for older versions. +if sys.version_info >= (3, 13): + from _typeshed import StrPath + from collections.abc import Iterator + from contextlib import AbstractContextManager + from importlib.resources._common import Anchor + from io import TextIOWrapper + from pathlib import Path + from typing import BinaryIO, overload + from typing_extensions import Unpack + + def open_binary(anchor: Anchor, *path_names: StrPath) -> BinaryIO: ... + @overload + def open_text( + anchor: Anchor, *path_names: Unpack[tuple[StrPath]], encoding: str | None = "utf-8", errors: str | None = "strict" + ) -> TextIOWrapper: ... + @overload + def open_text(anchor: Anchor, *path_names: StrPath, encoding: str | None, errors: str | None = "strict") -> TextIOWrapper: ... + def read_binary(anchor: Anchor, *path_names: StrPath) -> bytes: ... + @overload + def read_text( + anchor: Anchor, *path_names: Unpack[tuple[StrPath]], encoding: str | None = "utf-8", errors: str | None = "strict" + ) -> str: ... + @overload + def read_text(anchor: Anchor, *path_names: StrPath, encoding: str | None, errors: str | None = "strict") -> str: ... + def path(anchor: Anchor, *path_names: StrPath) -> AbstractContextManager[Path]: ... + def is_resource(anchor: Anchor, *path_names: StrPath) -> bool: ... + def contents(anchor: Anchor, *path_names: StrPath) -> Iterator[str]: ... diff --git a/mypy/typeshed/stdlib/importlib/resources/abc.pyi b/mypy/typeshed/stdlib/importlib/resources/abc.pyi index a36c952d01acf..ad80605f7c71d 100644 --- a/mypy/typeshed/stdlib/importlib/resources/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/resources/abc.pyi @@ -10,3 +10,5 @@ if sys.version_info >= (3, 11): Traversable as Traversable, TraversableResources as TraversableResources, ) + + __all__ = ["ResourceReader", "Traversable", "TraversableResources"] diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi index 2492c76d5c6ce..cc1c98ae4d0e4 100644 --- a/mypy/typeshed/stdlib/importlib/util.pyi +++ b/mypy/typeshed/stdlib/importlib/util.pyi @@ -2,10 +2,16 @@ import importlib.abc import importlib.machinery import sys import types -from _typeshed import ReadableBuffer, StrOrBytesPath -from _typeshed.importlib import LoaderProtocol +from _typeshed import ReadableBuffer from collections.abc import Callable -from typing import Any +from importlib._bootstrap import module_from_spec as module_from_spec, spec_from_loader as spec_from_loader +from importlib._bootstrap_external import ( + MAGIC_NUMBER as MAGIC_NUMBER, + cache_from_source as cache_from_source, + decode_source as decode_source, + source_from_cache as source_from_cache, + spec_from_file_location as spec_from_file_location, +) from typing_extensions import ParamSpec _P = ParamSpec("_P") @@ -16,24 +22,7 @@ if sys.version_info < (3, 12): def set_package(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ... def resolve_name(name: str, package: str | None) -> str: ... - -MAGIC_NUMBER: bytes - -def cache_from_source(path: str, debug_override: bool | None = None, *, optimization: Any | None = None) -> str: ... -def source_from_cache(path: str) -> str: ... -def decode_source(source_bytes: ReadableBuffer) -> str: ... def find_spec(name: str, package: str | None = None) -> importlib.machinery.ModuleSpec | None: ... -def spec_from_loader( - name: str, loader: LoaderProtocol | None, *, origin: str | None = None, is_package: bool | None = None -) -> importlib.machinery.ModuleSpec | None: ... -def spec_from_file_location( - name: str, - location: StrOrBytesPath | None = None, - *, - loader: LoaderProtocol | None = None, - submodule_search_locations: list[str] | None = ..., -) -> importlib.machinery.ModuleSpec | None: ... -def module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType: ... class LazyLoader(importlib.abc.Loader): def __init__(self, loader: importlib.abc.Loader) -> None: ... diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 23e0663d0d60c..1eb9fc502e125 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -25,7 +25,7 @@ from types import ( TracebackType, WrapperDescriptorType, ) -from typing import Any, ClassVar, Literal, NamedTuple, Protocol, TypeVar, overload +from typing import Any, ClassVar, Final, Literal, NamedTuple, Protocol, TypeVar, overload from typing_extensions import ParamSpec, Self, TypeAlias, TypeGuard, TypeIs if sys.version_info >= (3, 11): @@ -161,35 +161,39 @@ class BlockFinder: last: int def tokeneater(self, type: int, token: str, srowcol: tuple[int, int], erowcol: tuple[int, int], line: str) -> None: ... -CO_OPTIMIZED: Literal[1] -CO_NEWLOCALS: Literal[2] -CO_VARARGS: Literal[4] -CO_VARKEYWORDS: Literal[8] -CO_NESTED: Literal[16] -CO_GENERATOR: Literal[32] -CO_NOFREE: Literal[64] -CO_COROUTINE: Literal[128] -CO_ITERABLE_COROUTINE: Literal[256] -CO_ASYNC_GENERATOR: Literal[512] -TPFLAGS_IS_ABSTRACT: Literal[1048576] +CO_OPTIMIZED: Final = 1 +CO_NEWLOCALS: Final = 2 +CO_VARARGS: Final = 4 +CO_VARKEYWORDS: Final = 8 +CO_NESTED: Final = 16 +CO_GENERATOR: Final = 32 +CO_NOFREE: Final = 64 +CO_COROUTINE: Final = 128 +CO_ITERABLE_COROUTINE: Final = 256 +CO_ASYNC_GENERATOR: Final = 512 +TPFLAGS_IS_ABSTRACT: Final = 1048576 modulesbyfile: dict[str, Any] _GetMembersPredicateTypeGuard: TypeAlias = Callable[[Any], TypeGuard[_T]] +_GetMembersPredicateTypeIs: TypeAlias = Callable[[Any], TypeIs[_T]] _GetMembersPredicate: TypeAlias = Callable[[Any], bool] -_GetMembersReturnTypeGuard: TypeAlias = list[tuple[str, _T]] -_GetMembersReturn: TypeAlias = list[tuple[str, Any]] +_GetMembersReturn: TypeAlias = list[tuple[str, _T]] @overload -def getmembers(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturnTypeGuard[_T]: ... +def getmembers(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturn[_T]: ... @overload -def getmembers(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn: ... +def getmembers(object: object, predicate: _GetMembersPredicateTypeIs[_T]) -> _GetMembersReturn[_T]: ... +@overload +def getmembers(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn[Any]: ... if sys.version_info >= (3, 11): @overload - def getmembers_static(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturnTypeGuard[_T]: ... + def getmembers_static(object: object, predicate: _GetMembersPredicateTypeGuard[_T]) -> _GetMembersReturn[_T]: ... + @overload + def getmembers_static(object: object, predicate: _GetMembersPredicateTypeIs[_T]) -> _GetMembersReturn[_T]: ... @overload - def getmembers_static(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn: ... + def getmembers_static(object: object, predicate: _GetMembersPredicate | None = None) -> _GetMembersReturn[Any]: ... def getmodulename(path: StrPath) -> str | None: ... def ismodule(object: object) -> TypeIs[ModuleType]: ... @@ -360,10 +364,10 @@ class _ParameterKind(enum.IntEnum): def description(self) -> str: ... if sys.version_info >= (3, 12): - AGEN_CREATED: Literal["AGEN_CREATED"] - AGEN_RUNNING: Literal["AGEN_RUNNING"] - AGEN_SUSPENDED: Literal["AGEN_SUSPENDED"] - AGEN_CLOSED: Literal["AGEN_CLOSED"] + AGEN_CREATED: Final = "AGEN_CREATED" + AGEN_RUNNING: Final = "AGEN_RUNNING" + AGEN_SUSPENDED: Final = "AGEN_SUSPENDED" + AGEN_CLOSED: Final = "AGEN_CLOSED" def getasyncgenstate( agen: AsyncGenerator[Any, Any] @@ -580,19 +584,19 @@ def getattr_static(obj: object, attr: str, default: Any | None = ...) -> Any: .. # Current State of Generators and Coroutines # -GEN_CREATED: Literal["GEN_CREATED"] -GEN_RUNNING: Literal["GEN_RUNNING"] -GEN_SUSPENDED: Literal["GEN_SUSPENDED"] -GEN_CLOSED: Literal["GEN_CLOSED"] +GEN_CREATED: Final = "GEN_CREATED" +GEN_RUNNING: Final = "GEN_RUNNING" +GEN_SUSPENDED: Final = "GEN_SUSPENDED" +GEN_CLOSED: Final = "GEN_CLOSED" def getgeneratorstate( generator: Generator[Any, Any, Any] ) -> Literal["GEN_CREATED", "GEN_RUNNING", "GEN_SUSPENDED", "GEN_CLOSED"]: ... -CORO_CREATED: Literal["CORO_CREATED"] -CORO_RUNNING: Literal["CORO_RUNNING"] -CORO_SUSPENDED: Literal["CORO_SUSPENDED"] -CORO_CLOSED: Literal["CORO_CLOSED"] +CORO_CREATED: Final = "CORO_CREATED" +CORO_RUNNING: Final = "CORO_RUNNING" +CORO_SUSPENDED: Final = "CORO_SUSPENDED" +CORO_CLOSED: Final = "CORO_CLOSED" def getcoroutinestate( coroutine: Coroutine[Any, Any, Any] diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index 01f3bfc06a27a..5c26cb245a2f1 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -1,13 +1,26 @@ import abc -import builtins -import codecs import sys -from _typeshed import FileDescriptorOrPath, ReadableBuffer, WriteableBuffer -from collections.abc import Callable, Iterable, Iterator -from os import _Opener -from types import TracebackType -from typing import IO, Any, BinaryIO, Literal, Protocol, TextIO, TypeVar, overload, type_check_only -from typing_extensions import Self +from _io import ( + DEFAULT_BUFFER_SIZE as DEFAULT_BUFFER_SIZE, + BlockingIOError as BlockingIOError, + BufferedRandom as BufferedRandom, + BufferedReader as BufferedReader, + BufferedRWPair as BufferedRWPair, + BufferedWriter as BufferedWriter, + BytesIO as BytesIO, + FileIO as FileIO, + IncrementalNewlineDecoder as IncrementalNewlineDecoder, + StringIO as StringIO, + TextIOWrapper as TextIOWrapper, + _BufferedIOBase, + _IOBase, + _RawIOBase, + _TextIOBase, + _WrappedBuffer as _WrappedBuffer, # used elsewhere in typeshed + open as open, + open_code as open_code, +) +from typing import Final __all__ = [ "BlockingIOError", @@ -32,207 +45,16 @@ __all__ = [ ] if sys.version_info >= (3, 11): - __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] - -_T = TypeVar("_T") - -DEFAULT_BUFFER_SIZE: Literal[8192] + from _io import text_encoding as text_encoding -SEEK_SET: Literal[0] -SEEK_CUR: Literal[1] -SEEK_END: Literal[2] - -open = builtins.open - -def open_code(path: str) -> IO[bytes]: ... + __all__ += ["DEFAULT_BUFFER_SIZE", "IncrementalNewlineDecoder", "text_encoding"] -BlockingIOError = builtins.BlockingIOError +SEEK_SET: Final = 0 +SEEK_CUR: Final = 1 +SEEK_END: Final = 2 class UnsupportedOperation(OSError, ValueError): ... - -class IOBase(metaclass=abc.ABCMeta): - def __iter__(self) -> Iterator[bytes]: ... - def __next__(self) -> bytes: ... - def __enter__(self) -> Self: ... - def __exit__( - self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None - ) -> None: ... - def close(self) -> None: ... - def fileno(self) -> int: ... - def flush(self) -> None: ... - def isatty(self) -> bool: ... - def readable(self) -> bool: ... - read: Callable[..., Any] - def readlines(self, hint: int = -1, /) -> list[bytes]: ... - def seek(self, offset: int, whence: int = ..., /) -> int: ... - def seekable(self) -> bool: ... - def tell(self) -> int: ... - def truncate(self, size: int | None = ..., /) -> int: ... - def writable(self) -> bool: ... - write: Callable[..., Any] - def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ... - def readline(self, size: int | None = -1, /) -> bytes: ... - def __del__(self) -> None: ... - @property - def closed(self) -> bool: ... - def _checkClosed(self) -> None: ... # undocumented - -class RawIOBase(IOBase): - def readall(self) -> bytes: ... - def readinto(self, buffer: WriteableBuffer, /) -> int | None: ... - def write(self, b: ReadableBuffer, /) -> int | None: ... - def read(self, size: int = -1, /) -> bytes | None: ... - -class BufferedIOBase(IOBase): - raw: RawIOBase # This is not part of the BufferedIOBase API and may not exist on some implementations. - def detach(self) -> RawIOBase: ... - def readinto(self, buffer: WriteableBuffer, /) -> int: ... - def write(self, buffer: ReadableBuffer, /) -> int: ... - def readinto1(self, buffer: WriteableBuffer, /) -> int: ... - def read(self, size: int | None = ..., /) -> bytes: ... - def read1(self, size: int = ..., /) -> bytes: ... - -class FileIO(RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes - mode: str - # The type of "name" equals the argument passed in to the constructor, - # but that can make FileIO incompatible with other I/O types that assume - # "name" is a str. In the future, making FileIO generic might help. - name: Any - def __init__( - self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ... - ) -> None: ... - @property - def closefd(self) -> bool: ... - def write(self, b: ReadableBuffer, /) -> int: ... - def read(self, size: int = -1, /) -> bytes: ... - def __enter__(self) -> Self: ... - -class BytesIO(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes - def __init__(self, initial_bytes: ReadableBuffer = ...) -> None: ... - # 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 __enter__(self) -> Self: ... - def getvalue(self) -> bytes: ... - def getbuffer(self) -> memoryview: ... - def read1(self, size: int | None = -1, /) -> bytes: ... - -class BufferedReader(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes - def __enter__(self) -> Self: ... - def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... - def peek(self, size: int = 0, /) -> bytes: ... - -class BufferedWriter(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes - def __enter__(self) -> Self: ... - def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ... - def write(self, buffer: ReadableBuffer, /) -> int: ... - -class BufferedRandom(BufferedReader, BufferedWriter): # type: ignore[misc] # incompatible definitions of methods in the base classes - def __enter__(self) -> Self: ... - def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this - -class BufferedRWPair(BufferedIOBase): - def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = ...) -> None: ... - def peek(self, size: int = ..., /) -> bytes: ... - -class TextIOBase(IOBase): - encoding: str - errors: str | None - newlines: str | tuple[str, ...] | None - def __iter__(self) -> Iterator[str]: ... # type: ignore[override] - def __next__(self) -> str: ... # type: ignore[override] - def detach(self) -> BinaryIO: ... - def write(self, s: str, /) -> int: ... - def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] - def readline(self, size: int = ..., /) -> str: ... # type: ignore[override] - def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] - def read(self, size: int | None = ..., /) -> str: ... - -@type_check_only -class _WrappedBuffer(Protocol): - # "name" is wrapped by TextIOWrapper. Its type is inconsistent between - # the various I/O types, see the comments on TextIOWrapper.name and - # TextIO.name. - @property - def name(self) -> Any: ... - @property - def closed(self) -> bool: ... - def read(self, size: int = ..., /) -> ReadableBuffer: ... - # Optional: def read1(self, size: int, /) -> ReadableBuffer: ... - def write(self, b: bytes, /) -> object: ... - def flush(self) -> object: ... - def close(self) -> object: ... - def seekable(self) -> bool: ... - def readable(self) -> bool: ... - def writable(self) -> bool: ... - def truncate(self, size: int, /) -> int: ... - def fileno(self) -> int: ... - def isatty(self) -> int: ... - # Optional: Only needs to be present if seekable() returns True. - # def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ... - # def tell(self) -> int: ... - -# TODO: Should be generic over the buffer type, but needs to wait for -# TypeVar defaults. -class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible definitions of write in the base classes - def __init__( - self, - buffer: _WrappedBuffer, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - line_buffering: bool = False, - write_through: bool = False, - ) -> None: ... - # Equals the "buffer" argument passed in to the constructor. - @property - def buffer(self) -> BinaryIO: ... - @property - def closed(self) -> bool: ... - @property - def line_buffering(self) -> bool: ... - @property - def write_through(self) -> bool: ... - def reconfigure( - self, - *, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - line_buffering: bool | None = None, - write_through: bool | None = None, - ) -> None: ... - # These are inherited from TextIOBase, but must exist in the stub to satisfy mypy. - def __enter__(self) -> Self: ... - def __iter__(self) -> Iterator[str]: ... # type: ignore[override] - def __next__(self) -> str: ... # type: ignore[override] - def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override] - def readline(self, size: int = -1, /) -> str: ... # type: ignore[override] - def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override] - # Equals the "buffer" argument passed in to the constructor. - def detach(self) -> BinaryIO: ... - # TextIOWrapper's version of seek only supports a limited subset of - # operations. - def seek(self, cookie: int, whence: int = 0, /) -> int: ... - -class StringIO(TextIOWrapper): - def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ... - # 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) -> str: ... - -class IncrementalNewlineDecoder(codecs.IncrementalDecoder): - def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = ...) -> None: ... - def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ... - @property - def newlines(self) -> str | tuple[str, ...] | None: ... - def setstate(self, state: tuple[bytes, int], /) -> None: ... - -if sys.version_info >= (3, 10): - @overload - def text_encoding(encoding: None, stacklevel: int = 2, /) -> Literal["locale", "utf-8"]: ... - @overload - def text_encoding(encoding: _T, stacklevel: int = 2, /) -> _T: ... +class IOBase(_IOBase, metaclass=abc.ABCMeta): ... +class RawIOBase(_RawIOBase, IOBase): ... +class BufferedIOBase(_BufferedIOBase, IOBase): ... +class TextIOBase(_TextIOBase, IOBase): ... diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index 03decc74e65e7..f5cee43d6b328 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -1,11 +1,11 @@ import sys from collections.abc import Iterable, Iterator -from typing import Any, Generic, Literal, SupportsInt, TypeVar, overload +from typing import Any, Final, Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias # Undocumented length constants -IPV4LENGTH: Literal[32] -IPV6LENGTH: Literal[128] +IPV4LENGTH: Final = 32 +IPV6LENGTH: Final = 128 _A = TypeVar("_A", IPv4Address, IPv6Address) _N = TypeVar("_N", IPv4Network, IPv6Network) @@ -31,7 +31,7 @@ class _IPAddressBase: @property def version(self) -> int: ... -class _BaseAddress(_IPAddressBase, SupportsInt): +class _BaseAddress(_IPAddressBase): def __init__(self, address: object) -> None: ... def __add__(self, other: int) -> Self: ... def __hash__(self) -> int: ... @@ -51,25 +51,6 @@ class _BaseAddress(_IPAddressBase, SupportsInt): def __gt__(self, other: Self, NotImplemented: Any = ...) -> bool: ... def __le__(self, other: Self, NotImplemented: Any = ...) -> bool: ... - @property - def is_global(self) -> bool: ... - @property - def is_link_local(self) -> bool: ... - @property - def is_loopback(self) -> bool: ... - @property - def is_multicast(self) -> bool: ... - @property - def is_private(self) -> bool: ... - @property - def is_reserved(self) -> bool: ... - @property - def is_unspecified(self) -> bool: ... - @property - def max_prefixlen(self) -> int: ... - @property - def packed(self) -> bytes: ... - class _BaseNetwork(_IPAddressBase, Generic[_A]): network_address: _A netmask: _A @@ -109,8 +90,6 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]): @property def is_unspecified(self) -> bool: ... @property - def max_prefixlen(self) -> int: ... - @property def num_addresses(self) -> int: ... def overlaps(self, other: _BaseNetwork[IPv4Address] | _BaseNetwork[IPv6Address]) -> bool: ... @property @@ -128,19 +107,6 @@ class _BaseNetwork(_IPAddressBase, Generic[_A]): @property def hostmask(self) -> _A: ... -class _BaseInterface(_BaseAddress, Generic[_A, _N]): - hostmask: _A - netmask: _A - network: _N - @property - def ip(self) -> _A: ... - @property - def with_hostmask(self) -> str: ... - @property - def with_netmask(self) -> str: ... - @property - def with_prefixlen(self) -> str: ... - class _BaseV4: @property def version(self) -> Literal[4]: ... @@ -148,15 +114,43 @@ class _BaseV4: def max_prefixlen(self) -> Literal[32]: ... class IPv4Address(_BaseV4, _BaseAddress): + @property + def is_global(self) -> bool: ... + @property + def is_link_local(self) -> bool: ... + @property + def is_loopback(self) -> bool: ... + @property + def is_multicast(self) -> bool: ... + @property + def is_private(self) -> bool: ... + @property + def is_reserved(self) -> bool: ... + @property + def is_unspecified(self) -> bool: ... + @property + def packed(self) -> bytes: ... if sys.version_info >= (3, 13): @property def ipv6_mapped(self) -> IPv6Address: ... class IPv4Network(_BaseV4, _BaseNetwork[IPv4Address]): ... -class IPv4Interface(IPv4Address, _BaseInterface[IPv4Address, IPv4Network]): +class IPv4Interface(IPv4Address): + netmask: IPv4Address + network: IPv4Network def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... + @property + def hostmask(self) -> IPv4Address: ... + @property + def ip(self) -> IPv4Address: ... + @property + def with_hostmask(self) -> str: ... + @property + def with_netmask(self) -> str: ... + @property + def with_prefixlen(self) -> str: ... class _BaseV6: @property @@ -165,6 +159,22 @@ class _BaseV6: def max_prefixlen(self) -> Literal[128]: ... class IPv6Address(_BaseV6, _BaseAddress): + @property + def is_global(self) -> bool: ... + @property + def is_link_local(self) -> bool: ... + @property + def is_loopback(self) -> bool: ... + @property + def is_multicast(self) -> bool: ... + @property + def is_private(self) -> bool: ... + @property + def is_reserved(self) -> bool: ... + @property + def is_unspecified(self) -> bool: ... + @property + def packed(self) -> bytes: ... @property def ipv4_mapped(self) -> IPv4Address | None: ... @property @@ -184,9 +194,21 @@ class IPv6Network(_BaseV6, _BaseNetwork[IPv6Address]): @property def is_site_local(self) -> bool: ... -class IPv6Interface(IPv6Address, _BaseInterface[IPv6Address, IPv6Network]): +class IPv6Interface(IPv6Address): + netmask: IPv6Address + network: IPv6Network def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... + @property + def hostmask(self) -> IPv6Address: ... + @property + def ip(self) -> IPv6Address: ... + @property + def with_hostmask(self) -> str: ... + @property + def with_netmask(self) -> str: ... + @property + def with_prefixlen(self) -> str: ... def v4_int_to_packed(address: int) -> bytes: ... def v6_int_to_packed(address: int) -> bytes: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index 16e04829c6cf9..f696658824985 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -1,4 +1,5 @@ import sys +from _typeshed import MaybeNone from collections.abc import Callable, Iterable, Iterator from typing import Any, Generic, Literal, SupportsComplex, SupportsFloat, SupportsIndex, SupportsInt, TypeVar, overload from typing_extensions import Self, TypeAlias @@ -122,7 +123,7 @@ class zip_longest(Iterator[_T_co]): # In the overloads without fillvalue, all of the tuple members could theoretically be None, # but we return Any instead to avoid false positives for code where we know one of the iterables # is longer. - def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> zip_longest[tuple[_T1 | Any, _T2 | Any]]: ... + def __new__(cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone]]: ... @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], /, *, fillvalue: _T @@ -131,7 +132,7 @@ class zip_longest(Iterator[_T_co]): @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], / - ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any]]: ... + ) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone, _T3 | MaybeNone]]: ... @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], /, *, fillvalue: _T @@ -140,7 +141,7 @@ class zip_longest(Iterator[_T_co]): @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], / - ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any]]: ... + ) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone, _T3 | MaybeNone, _T4 | MaybeNone]]: ... @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], /, *, fillvalue: _T @@ -149,7 +150,7 @@ class zip_longest(Iterator[_T_co]): @overload def __new__( cls, iter1: Iterable[_T1], iter2: Iterable[_T2], iter3: Iterable[_T3], iter4: Iterable[_T4], iter5: Iterable[_T5], / - ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any, _T5 | Any]]: ... + ) -> zip_longest[tuple[_T1 | MaybeNone, _T2 | MaybeNone, _T3 | MaybeNone, _T4 | MaybeNone, _T5 | MaybeNone]]: ... @overload def __new__( cls, @@ -174,7 +175,7 @@ class zip_longest(Iterator[_T_co]): iter6: Iterable[_T], /, *iterables: Iterable[_T], - ) -> zip_longest[tuple[_T | Any, ...]]: ... + ) -> zip_longest[tuple[_T | MaybeNone, ...]]: ... @overload def __new__( cls, @@ -326,6 +327,10 @@ if sys.version_info >= (3, 10): if sys.version_info >= (3, 12): class batched(Iterator[tuple[_T_co, ...]], Generic[_T_co]): - def __new__(cls, iterable: Iterable[_T_co], n: int) -> Self: ... + if sys.version_info >= (3, 13): + def __new__(cls, iterable: Iterable[_T_co], n: int, *, strict: bool = False) -> Self: ... + else: + def __new__(cls, iterable: Iterable[_T_co], n: int) -> Self: ... + def __iter__(self) -> Self: ... def __next__(self) -> tuple[_T_co, ...]: ... diff --git a/mypy/typeshed/stdlib/json/encoder.pyi b/mypy/typeshed/stdlib/json/encoder.pyi index 473398a60b2a0..aa4a3bdf61d4f 100644 --- a/mypy/typeshed/stdlib/json/encoder.pyi +++ b/mypy/typeshed/stdlib/json/encoder.pyi @@ -1,12 +1,12 @@ from collections.abc import Callable, Iterator from re import Pattern -from typing import Any +from typing import Any, Final -ESCAPE: Pattern[str] -ESCAPE_ASCII: Pattern[str] -HAS_UTF8: Pattern[bytes] -ESCAPE_DCT: dict[str, str] -INFINITY: float +ESCAPE: Final[Pattern[str]] +ESCAPE_ASCII: Final[Pattern[str]] +HAS_UTF8: Final[Pattern[bytes]] +ESCAPE_DCT: Final[dict[str, str]] +INFINITY: Final[float] def py_encode_basestring(s: str) -> str: ... # undocumented def py_encode_basestring_ascii(s: str) -> str: ... # undocumented diff --git a/mypy/typeshed/stdlib/json/scanner.pyi b/mypy/typeshed/stdlib/json/scanner.pyi new file mode 100644 index 0000000000000..f3b98996b7523 --- /dev/null +++ b/mypy/typeshed/stdlib/json/scanner.pyi @@ -0,0 +1,3 @@ +from _json import make_scanner as make_scanner + +__all__ = ["make_scanner"] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi index fb0b472aa12ac..1bf7db2f76e98 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_asserts.pyi @@ -1,8 +1,8 @@ -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from ..fixer_base import BaseFix -NAMES: dict[str, str] +NAMES: Final[dict[str, str]] class FixAsserts(BaseFix): BM_compatible: ClassVar[Literal[False]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi index 4595c57c7eb92..6b2723d09d436 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_idioms.pyi @@ -1,9 +1,9 @@ -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base -CMP: str -TYPE: str +CMP: Final[str] +TYPE: Final[str] class FixIdioms(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[False]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi index dd6f72dd88ac2..c747af529f440 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports.pyi @@ -1,11 +1,11 @@ from _typeshed import StrPath from collections.abc import Generator -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base from ..pytree import Node -MAPPING: dict[str, str] +MAPPING: Final[dict[str, str]] def alternates(members): ... def build_pattern(mapping=...) -> Generator[str, None, None]: ... diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi index 8d55433085dd4..618ecd0424d86 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_imports2.pyi @@ -1,6 +1,8 @@ +from typing import Final + from . import fix_imports -MAPPING: dict[str, str] +MAPPING: Final[dict[str, str]] class FixImports2(fix_imports.FixImports): mapping = MAPPING diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi index 594b5e2c95c95..ca9b71e43f856 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_methodattrs.pyi @@ -1,8 +1,8 @@ -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base -MAP: dict[str, str] +MAP: Final[dict[str, str]] class FixMethodattrs(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi index 6283f1ab7ce21..652d8f15ea1a9 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_renames.pyi @@ -1,10 +1,10 @@ from collections.abc import Generator -from typing import ClassVar, Literal +from typing import ClassVar, Final, Literal from .. import fixer_base -MAPPING: dict[str, dict[str, str]] -LOOKUP: dict[tuple[str, str], str] +MAPPING: Final[dict[str, dict[str, str]]] +LOOKUP: Final[dict[tuple[str, str], str]] def alternates(members): ... def build_pattern() -> Generator[str, None, None]: ... diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi index 80d9d8b6e6560..85d1315213b96 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_unicode.pyi @@ -6,7 +6,7 @@ from ..pytree import Node class FixUnicode(fixer_base.BaseFix): BM_compatible: ClassVar[Literal[True]] - PATTERN: ClassVar[Literal["STRING | 'unicode' | 'unichr'"]] # type: ignore[name-defined] # Name "STRING" is not defined + PATTERN: ClassVar[str] unicode_literals: bool def start_tree(self, tree: Node, filename: StrPath) -> None: ... def transform(self, node, results): ... diff --git a/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi b/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi index 625472f609ab4..abdcc0f62970f 100644 --- a/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi +++ b/mypy/typeshed/stdlib/lib2to3/fixes/fix_urllib.pyi @@ -1,9 +1,9 @@ from collections.abc import Generator -from typing import Literal +from typing import Final, Literal from .fix_imports import FixImports -MAPPING: dict[str, list[tuple[Literal["urllib.request", "urllib.parse", "urllib.error"], list[str]]]] +MAPPING: Final[dict[str, list[tuple[Literal["urllib.request", "urllib.parse", "urllib.error"], list[str]]]]] def build_pattern() -> Generator[str, None, None]: ... diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi index debcb2193987d..6898517acee64 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/token.pyi @@ -1,65 +1,67 @@ -ENDMARKER: int -NAME: int -NUMBER: int -STRING: int -NEWLINE: int -INDENT: int -DEDENT: int -LPAR: int -RPAR: int -LSQB: int -RSQB: int -COLON: int -COMMA: int -SEMI: int -PLUS: int -MINUS: int -STAR: int -SLASH: int -VBAR: int -AMPER: int -LESS: int -GREATER: int -EQUAL: int -DOT: int -PERCENT: int -BACKQUOTE: int -LBRACE: int -RBRACE: int -EQEQUAL: int -NOTEQUAL: int -LESSEQUAL: int -GREATEREQUAL: int -TILDE: int -CIRCUMFLEX: int -LEFTSHIFT: int -RIGHTSHIFT: int -DOUBLESTAR: int -PLUSEQUAL: int -MINEQUAL: int -STAREQUAL: int -SLASHEQUAL: int -PERCENTEQUAL: int -AMPEREQUAL: int -VBAREQUAL: int -CIRCUMFLEXEQUAL: int -LEFTSHIFTEQUAL: int -RIGHTSHIFTEQUAL: int -DOUBLESTAREQUAL: int -DOUBLESLASH: int -DOUBLESLASHEQUAL: int -OP: int -COMMENT: int -NL: int -RARROW: int -AT: int -ATEQUAL: int -AWAIT: int -ASYNC: int -ERRORTOKEN: int -COLONEQUAL: int -N_TOKENS: int -NT_OFFSET: int +from typing import Final + +ENDMARKER: Final[int] +NAME: Final[int] +NUMBER: Final[int] +STRING: Final[int] +NEWLINE: Final[int] +INDENT: Final[int] +DEDENT: Final[int] +LPAR: Final[int] +RPAR: Final[int] +LSQB: Final[int] +RSQB: Final[int] +COLON: Final[int] +COMMA: Final[int] +SEMI: Final[int] +PLUS: Final[int] +MINUS: Final[int] +STAR: Final[int] +SLASH: Final[int] +VBAR: Final[int] +AMPER: Final[int] +LESS: Final[int] +GREATER: Final[int] +EQUAL: Final[int] +DOT: Final[int] +PERCENT: Final[int] +BACKQUOTE: Final[int] +LBRACE: Final[int] +RBRACE: Final[int] +EQEQUAL: Final[int] +NOTEQUAL: Final[int] +LESSEQUAL: Final[int] +GREATEREQUAL: Final[int] +TILDE: Final[int] +CIRCUMFLEX: Final[int] +LEFTSHIFT: Final[int] +RIGHTSHIFT: Final[int] +DOUBLESTAR: Final[int] +PLUSEQUAL: Final[int] +MINEQUAL: Final[int] +STAREQUAL: Final[int] +SLASHEQUAL: Final[int] +PERCENTEQUAL: Final[int] +AMPEREQUAL: Final[int] +VBAREQUAL: Final[int] +CIRCUMFLEXEQUAL: Final[int] +LEFTSHIFTEQUAL: Final[int] +RIGHTSHIFTEQUAL: Final[int] +DOUBLESTAREQUAL: Final[int] +DOUBLESLASH: Final[int] +DOUBLESLASHEQUAL: Final[int] +OP: Final[int] +COMMENT: Final[int] +NL: Final[int] +RARROW: Final[int] +AT: Final[int] +ATEQUAL: Final[int] +AWAIT: Final[int] +ASYNC: Final[int] +ERRORTOKEN: Final[int] +COLONEQUAL: Final[int] +N_TOKENS: Final[int] +NT_OFFSET: Final[int] tok_name: dict[int, str] def ISTERMINAL(x: int) -> bool: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 4c6163257236f..9a4827a8f626f 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -7,7 +7,7 @@ from re import Pattern from string import Template from time import struct_time from types import FrameType, TracebackType -from typing import Any, ClassVar, Generic, Literal, Protocol, TextIO, TypeVar, overload +from typing import Any, ClassVar, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 11): @@ -55,10 +55,9 @@ __all__ = [ "setLogRecordFactory", "lastResort", "raiseExceptions", + "warn", ] -if sys.version_info < (3, 13): - __all__ += ["warn"] if sys.version_info >= (3, 11): __all__ += ["getLevelNamesMapping"] if sys.version_info >= (3, 12): @@ -157,17 +156,16 @@ class Logger(Filterer): stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... - if sys.version_info < (3, 13): - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - + @deprecated("Deprecated; use warning() instead.") + def warn( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + ) -> None: ... def error( self, msg: object, @@ -236,14 +234,14 @@ class Logger(Filterer): def hasHandlers(self) -> bool: ... def callHandlers(self, record: LogRecord) -> None: ... # undocumented -CRITICAL: int -FATAL: int -ERROR: int -WARNING: int -WARN: int -INFO: int -DEBUG: int -NOTSET: int +CRITICAL: Final = 50 +FATAL: Final = CRITICAL +ERROR: Final = 40 +WARNING: Final = 30 +WARN: Final = WARNING +INFO: Final = 20 +DEBUG: Final = 10 +NOTSET: Final = 0 class Handler(Filterer): level: int # undocumented @@ -412,18 +410,17 @@ class LoggerAdapter(Generic[_L]): extra: Mapping[str, object] | None = None, **kwargs: object, ) -> None: ... - if sys.version_info < (3, 13): - def warn( - self, - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - **kwargs: object, - ) -> None: ... - + @deprecated("Deprecated; use warning() instead.") + def warn( + self, + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, + **kwargs: object, + ) -> None: ... def error( self, msg: object, @@ -523,17 +520,15 @@ def warning( stacklevel: int = 1, extra: Mapping[str, object] | None = None, ) -> None: ... - -if sys.version_info < (3, 13): - def warn( - msg: object, - *args: object, - exc_info: _ExcInfoType = None, - stack_info: bool = False, - stacklevel: int = 1, - extra: Mapping[str, object] | None = None, - ) -> None: ... - +@deprecated("Deprecated; use warning() instead.") +def warn( + msg: object, + *args: object, + exc_info: _ExcInfoType = None, + stack_info: bool = False, + stacklevel: int = 1, + extra: Mapping[str, object] | None = None, +) -> None: ... def error( msg: object, *args: object, @@ -684,6 +679,6 @@ class StrFormatStyle(PercentStyle): # undocumented class StringTemplateStyle(PercentStyle): # undocumented _tpl: Template -_STYLES: dict[str, tuple[PercentStyle, str]] +_STYLES: Final[dict[str, tuple[PercentStyle, str]]] -BASIC_FORMAT: str +BASIC_FORMAT: Final[str] diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index 7a26846addbbc..5c444e66c4c7b 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -1,17 +1,17 @@ import sys from _typeshed import StrOrBytesPath -from collections.abc import Callable, Hashable, Iterable, Sequence +from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence from configparser import RawConfigParser from re import Pattern from threading import Thread -from typing import IO, Any, Literal, SupportsIndex, TypedDict, overload +from typing import IO, Any, Final, Literal, SupportsIndex, TypedDict, overload from typing_extensions import Required, TypeAlias from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level DEFAULT_LOGGING_CONFIG_PORT: int -RESET_ERROR: int # undocumented -IDENTIFIER: Pattern[str] # undocumented +RESET_ERROR: Final[int] # undocumented +IDENTIFIER: Final[Pattern[str]] # undocumented if sys.version_info >= (3, 11): class _RootLoggerConfiguration(TypedDict, total=False): @@ -63,7 +63,7 @@ def dictConfig(config: _DictConfigArgs | dict[str, Any]) -> None: ... if sys.version_info >= (3, 10): def fileConfig( fname: StrOrBytesPath | IO[str] | RawConfigParser, - defaults: dict[str, str] | None = None, + defaults: Mapping[str, str] | None = None, disable_existing_loggers: bool = True, encoding: str | None = None, ) -> None: ... @@ -71,7 +71,7 @@ if sys.version_info >= (3, 10): else: def fileConfig( fname: StrOrBytesPath | IO[str] | RawConfigParser, - defaults: dict[str, str] | None = None, + defaults: Mapping[str, str] | None = None, disable_existing_loggers: bool = True, ) -> None: ... @@ -116,7 +116,7 @@ class BaseConfigurator: # undocumented def cfg_convert(self, value: str) -> Any: ... def convert(self, value: Any) -> Any: ... def configure_custom(self, config: dict[str, Any]) -> Any: ... - def as_tuple(self, value: list[Any] | tuple[Any]) -> tuple[Any]: ... + def as_tuple(self, value: list[Any] | tuple[Any, ...]) -> tuple[Any, ...]: ... class DictConfigurator(BaseConfigurator): def configure(self) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/logging/handlers.pyi b/mypy/typeshed/stdlib/logging/handlers.pyi index 4e97012abba11..d594d6569a7e3 100644 --- a/mypy/typeshed/stdlib/logging/handlers.pyi +++ b/mypy/typeshed/stdlib/logging/handlers.pyi @@ -8,16 +8,16 @@ from logging import FileHandler, Handler, LogRecord from re import Pattern from socket import SocketKind, socket from threading import Thread -from typing import Any, ClassVar, Protocol, TypeVar +from typing import Any, ClassVar, Final, Protocol, TypeVar _T = TypeVar("_T") -DEFAULT_TCP_LOGGING_PORT: int -DEFAULT_UDP_LOGGING_PORT: int -DEFAULT_HTTP_LOGGING_PORT: int -DEFAULT_SOAP_LOGGING_PORT: int -SYSLOG_UDP_PORT: int -SYSLOG_TCP_PORT: int +DEFAULT_TCP_LOGGING_PORT: Final[int] +DEFAULT_UDP_LOGGING_PORT: Final[int] +DEFAULT_HTTP_LOGGING_PORT: Final[int] +DEFAULT_SOAP_LOGGING_PORT: Final[int] +SYSLOG_UDP_PORT: Final[int] +SYSLOG_TCP_PORT: Final[int] class WatchedFileHandler(FileHandler): dev: int # undocumented @@ -260,6 +260,8 @@ class QueueHandler(Handler): def __init__(self, queue: _QueueLike[Any]) -> None: ... def prepare(self, record: LogRecord) -> Any: ... def enqueue(self, record: LogRecord) -> None: ... + if sys.version_info >= (3, 12): + listener: QueueListener | None class QueueListener: handlers: tuple[Handler, ...] # undocumented diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index c05e46a02aeb2..2f0279f5986bd 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -1,7 +1,41 @@ from _compression import BaseStream +from _lzma import ( + CHECK_CRC32 as CHECK_CRC32, + CHECK_CRC64 as CHECK_CRC64, + CHECK_ID_MAX as CHECK_ID_MAX, + CHECK_NONE as CHECK_NONE, + CHECK_SHA256 as CHECK_SHA256, + CHECK_UNKNOWN as CHECK_UNKNOWN, + FILTER_ARM as FILTER_ARM, + FILTER_ARMTHUMB as FILTER_ARMTHUMB, + FILTER_DELTA as FILTER_DELTA, + FILTER_IA64 as FILTER_IA64, + FILTER_LZMA1 as FILTER_LZMA1, + FILTER_LZMA2 as FILTER_LZMA2, + FILTER_POWERPC as FILTER_POWERPC, + FILTER_SPARC as FILTER_SPARC, + FILTER_X86 as FILTER_X86, + FORMAT_ALONE as FORMAT_ALONE, + FORMAT_AUTO as FORMAT_AUTO, + FORMAT_RAW as FORMAT_RAW, + FORMAT_XZ as FORMAT_XZ, + MF_BT2 as MF_BT2, + MF_BT3 as MF_BT3, + MF_BT4 as MF_BT4, + MF_HC3 as MF_HC3, + MF_HC4 as MF_HC4, + MODE_FAST as MODE_FAST, + MODE_NORMAL as MODE_NORMAL, + PRESET_DEFAULT as PRESET_DEFAULT, + PRESET_EXTREME as PRESET_EXTREME, + LZMACompressor as LZMACompressor, + LZMADecompressor as LZMADecompressor, + LZMAError as LZMAError, + _FilterChain, + is_check_supported as is_check_supported, +) from _typeshed import ReadableBuffer, StrOrBytesPath -from collections.abc import Mapping, Sequence -from typing import IO, Any, Literal, TextIO, final, overload +from typing import IO, Literal, TextIO, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -48,62 +82,6 @@ _OpenTextWritingMode: TypeAlias = Literal["wt", "xt", "at"] _PathOrFile: TypeAlias = StrOrBytesPath | IO[bytes] -_FilterChain: TypeAlias = Sequence[Mapping[str, Any]] - -FORMAT_AUTO: Literal[0] -FORMAT_XZ: Literal[1] -FORMAT_ALONE: Literal[2] -FORMAT_RAW: Literal[3] -CHECK_NONE: Literal[0] -CHECK_CRC32: Literal[1] -CHECK_CRC64: Literal[4] -CHECK_SHA256: Literal[10] -CHECK_ID_MAX: Literal[15] -CHECK_UNKNOWN: Literal[16] -FILTER_LZMA1: int # v big number -FILTER_LZMA2: Literal[33] -FILTER_DELTA: Literal[3] -FILTER_X86: Literal[4] -FILTER_IA64: Literal[6] -FILTER_ARM: Literal[7] -FILTER_ARMTHUMB: Literal[8] -FILTER_SPARC: Literal[9] -FILTER_POWERPC: Literal[5] -MF_HC3: Literal[3] -MF_HC4: Literal[4] -MF_BT2: Literal[18] -MF_BT3: Literal[19] -MF_BT4: Literal[20] -MODE_FAST: Literal[1] -MODE_NORMAL: Literal[2] -PRESET_DEFAULT: Literal[6] -PRESET_EXTREME: int # v big number - -# from _lzma.c -@final -class LZMADecompressor: - def __init__(self, format: int | None = ..., memlimit: int | None = ..., filters: _FilterChain | None = ...) -> None: ... - def decompress(self, data: ReadableBuffer, max_length: int = -1) -> bytes: ... - @property - def check(self) -> int: ... - @property - def eof(self) -> bool: ... - @property - def unused_data(self) -> bytes: ... - @property - def needs_input(self) -> bool: ... - -# from _lzma.c -@final -class LZMACompressor: - def __init__( - self, format: int | None = ..., check: int = ..., preset: int | None = ..., filters: _FilterChain | None = ... - ) -> None: ... - def compress(self, data: ReadableBuffer, /) -> bytes: ... - def flush(self) -> bytes: ... - -class LZMAError(Exception): ... - class LZMAFile(BaseStream, IO[bytes]): # type: ignore[misc] # incompatible definitions of writelines in the base classes def __init__( self, @@ -194,4 +172,3 @@ def compress( def decompress( data: ReadableBuffer, format: int = 0, memlimit: int | None = None, filters: _FilterChain | None = None ) -> bytes: ... -def is_check_supported(check_id: int, /) -> bool: ... diff --git a/mypy/typeshed/stdlib/mailbox.pyi b/mypy/typeshed/stdlib/mailbox.pyi index 2f43f9552652c..a98a00a42853e 100644 --- a/mypy/typeshed/stdlib/mailbox.pyi +++ b/mypy/typeshed/stdlib/mailbox.pyi @@ -115,6 +115,14 @@ class Maildir(Mailbox[MaildirMessage]): def get_message(self, key: str) -> MaildirMessage: ... def get_bytes(self, key: str) -> bytes: ... def get_file(self, key: str) -> _ProxyFile[bytes]: ... + if sys.version_info >= (3, 13): + def get_info(self, key: str) -> str: ... + def set_info(self, key: str, info: str) -> None: ... + def get_flags(self, key: str) -> str: ... + def set_flags(self, key: str, flags: str) -> None: ... + def add_flag(self, key: str, flag: str) -> None: ... + def remove_flag(self, key: str, flag: str) -> None: ... + def iterkeys(self) -> Iterator[str]: ... def __contains__(self, key: str) -> bool: ... def __len__(self) -> int: ... diff --git a/mypy/typeshed/stdlib/mimetypes.pyi b/mypy/typeshed/stdlib/mimetypes.pyi index 517193e3516f1..9914a34a2d6a6 100644 --- a/mypy/typeshed/stdlib/mimetypes.pyi +++ b/mypy/typeshed/stdlib/mimetypes.pyi @@ -45,6 +45,7 @@ class MimeTypes: types_map: tuple[dict[str, str], dict[str, str]] types_map_inv: tuple[dict[str, str], dict[str, str]] def __init__(self, filenames: tuple[str, ...] = (), strict: bool = True) -> None: ... + def add_type(self, type: str, ext: str, strict: bool = True) -> None: ... def guess_extension(self, type: str, strict: bool = True) -> str | None: ... def guess_type(self, url: StrPath, strict: bool = True) -> tuple[str | None, str | None]: ... def guess_all_extensions(self, type: str, strict: bool = True) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 7688970e57863..f94e876237d19 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -1,7 +1,7 @@ import sys from _typeshed import ReadableBuffer, Unused -from collections.abc import Iterable, Iterator, Sized -from typing import Final, NoReturn, overload +from collections.abc import Iterator +from typing import Final, Literal, NoReturn, overload from typing_extensions import Self ACCESS_DEFAULT: int @@ -30,13 +30,26 @@ if sys.platform != "win32": PAGESIZE: int -class mmap(Iterable[int], Sized): +class mmap: if sys.platform == "win32": def __init__(self, fileno: int, length: int, tagname: str | None = ..., access: int = ..., offset: int = ...) -> None: ... else: - def __init__( - self, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ... - ) -> None: ... + if sys.version_info >= (3, 13): + def __init__( + self, + fileno: int, + length: int, + flags: int = ..., + prot: int = ..., + access: int = ..., + offset: int = ..., + *, + trackfd: bool = True, + ) -> None: ... + else: + def __init__( + self, fileno: int, length: int, flags: int = ..., prot: int = ..., access: int = ..., offset: int = ... + ) -> None: ... def close(self) -> None: ... def flush(self, offset: int = ..., size: int = ...) -> None: ... @@ -77,7 +90,7 @@ class mmap(Iterable[int], Sized): def __buffer__(self, flags: int, /) -> memoryview: ... def __release_buffer__(self, buffer: memoryview, /) -> None: ... if sys.version_info >= (3, 13): - def seekable(self) -> bool: ... + def seekable(self) -> Literal[True]: ... if sys.platform != "win32": MADV_NORMAL: int @@ -118,4 +131,16 @@ if sys.version_info >= (3, 13) and sys.platform != "win32": MAP_32BIT: Final = 32768 if sys.version_info >= (3, 13) and sys.platform == "darwin": + MAP_NORESERVE: Final = 64 + MAP_NOEXTEND: Final = 256 + MAP_HASSEMAPHORE: Final = 512 + MAP_NOCACHE: Final = 1024 + MAP_JIT: Final = 2048 + MAP_RESILIENT_CODESIGN: Final = 8192 + MAP_RESILIENT_MEDIA: Final = 16384 + MAP_TRANSLATED_ALLOW_EXECUTE: Final = 131072 + MAP_UNIX03: Final = 262144 MAP_TPRO: Final = 524288 + +if sys.version_info >= (3, 13) and sys.platform == "linux": + MAP_NORESERVE: Final = 16384 diff --git a/mypy/typeshed/stdlib/modulefinder.pyi b/mypy/typeshed/stdlib/modulefinder.pyi index 132cac5f18785..6db665a18e691 100644 --- a/mypy/typeshed/stdlib/modulefinder.pyi +++ b/mypy/typeshed/stdlib/modulefinder.pyi @@ -1,15 +1,15 @@ import sys from collections.abc import Container, Iterable, Iterator, Sequence from types import CodeType -from typing import IO, Any +from typing import IO, Any, Final if sys.version_info < (3, 11): - LOAD_CONST: int # undocumented - IMPORT_NAME: int # undocumented - STORE_NAME: int # undocumented - STORE_GLOBAL: int # undocumented - STORE_OPS: tuple[int, int] # undocumented - EXTENDED_ARG: int # undocumented + LOAD_CONST: Final[int] # undocumented + IMPORT_NAME: Final[int] # undocumented + STORE_NAME: Final[int] # undocumented + STORE_GLOBAL: Final[int] # undocumented + STORE_OPS: Final[tuple[int, int]] # undocumented + EXTENDED_ARG: Final[int] # undocumented packagePathMap: dict[str, list[str]] # undocumented @@ -64,3 +64,5 @@ class ModuleFinder: def any_missing(self) -> list[str]: ... # undocumented def any_missing_maybe(self) -> tuple[list[str], list[str]]: ... # undocumented def replace_paths_in_code(self, co: CodeType) -> CodeType: ... # undocumented + +def test() -> ModuleFinder | None: ... # undocumented diff --git a/mypy/typeshed/stdlib/msvcrt.pyi b/mypy/typeshed/stdlib/msvcrt.pyi index 54b3674a3a460..403a5d9335227 100644 --- a/mypy/typeshed/stdlib/msvcrt.pyi +++ b/mypy/typeshed/stdlib/msvcrt.pyi @@ -1,14 +1,14 @@ import sys -from typing import Final, Literal +from typing import Final # This module is only available on Windows if sys.platform == "win32": CRT_ASSEMBLY_VERSION: Final[str] - LK_UNLCK: Literal[0] - LK_LOCK: Literal[1] - LK_NBLCK: Literal[2] - LK_RLCK: Literal[3] - LK_NBRLCK: Literal[4] + LK_UNLCK: Final = 0 + LK_LOCK: Final = 1 + LK_NBLCK: Final = 2 + LK_RLCK: Final = 3 + LK_NBRLCK: Final = 4 SEM_FAILCRITICALERRORS: int SEM_NOALIGNMENTFAULTEXCEPT: int SEM_NOGPFAULTERRORBOX: int diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index 7045a81b85be3..9998239d3119d 100644 --- a/mypy/typeshed/stdlib/multiprocessing/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/connection.pyi @@ -1,9 +1,9 @@ import socket import sys -import types -from _typeshed import ReadableBuffer +from _typeshed import Incomplete, ReadableBuffer from collections.abc import Iterable -from typing import Any, SupportsIndex +from types import TracebackType +from typing import Any, Generic, SupportsIndex, TypeVar from typing_extensions import Self, TypeAlias __all__ = ["Client", "Listener", "Pipe", "wait"] @@ -11,7 +11,11 @@ __all__ = ["Client", "Listener", "Pipe", "wait"] # https://docs.python.org/3/library/multiprocessing.html#address-formats _Address: TypeAlias = str | tuple[str, int] -class _ConnectionBase: +# Defaulting to Any to avoid forcing generics on a lot of pre-existing code +_SendT = TypeVar("_SendT", contravariant=True, default=Any) +_RecvT = TypeVar("_RecvT", covariant=True, default=Any) + +class _ConnectionBase(Generic[_SendT, _RecvT]): def __init__(self, handle: SupportsIndex, readable: bool = True, writable: bool = True) -> None: ... @property def closed(self) -> bool: ... # undocumented @@ -22,27 +26,27 @@ class _ConnectionBase: def fileno(self) -> int: ... def close(self) -> None: ... def send_bytes(self, buf: ReadableBuffer, offset: int = 0, size: int | None = None) -> None: ... - def send(self, obj: Any) -> None: ... + def send(self, obj: _SendT) -> None: ... def recv_bytes(self, maxlength: int | None = None) -> bytes: ... def recv_bytes_into(self, buf: Any, offset: int = 0) -> int: ... - def recv(self) -> Any: ... + def recv(self) -> _RecvT: ... def poll(self, timeout: float | None = 0.0) -> bool: ... def __enter__(self) -> Self: ... def __exit__( - self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: types.TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: TracebackType | None ) -> None: ... def __del__(self) -> None: ... -class Connection(_ConnectionBase): ... +class Connection(_ConnectionBase[_SendT, _RecvT]): ... if sys.platform == "win32": - class PipeConnection(_ConnectionBase): ... + class PipeConnection(_ConnectionBase[_SendT, _RecvT]): ... class Listener: def __init__( self, address: _Address | None = None, family: str | None = None, backlog: int = 1, authkey: bytes | None = None ) -> None: ... - def accept(self) -> Connection: ... + def accept(self) -> Connection[Incomplete, Incomplete]: ... def close(self) -> None: ... @property def address(self) -> _Address: ... @@ -50,26 +54,30 @@ class Listener: def last_accepted(self) -> _Address | None: ... def __enter__(self) -> Self: ... def __exit__( - self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: types.TracebackType | None + self, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_tb: TracebackType | None ) -> None: ... +# Any: send and recv methods unused if sys.version_info >= (3, 12): - def deliver_challenge(connection: Connection, authkey: bytes, digest_name: str = "sha256") -> None: ... + def deliver_challenge(connection: Connection[Any, Any], authkey: bytes, digest_name: str = "sha256") -> None: ... else: - def deliver_challenge(connection: Connection, authkey: bytes) -> None: ... + def deliver_challenge(connection: Connection[Any, Any], authkey: bytes) -> None: ... -def answer_challenge(connection: Connection, authkey: bytes) -> None: ... +def answer_challenge(connection: Connection[Any, Any], authkey: bytes) -> None: ... def wait( - object_list: Iterable[Connection | socket.socket | int], timeout: float | None = None -) -> list[Connection | socket.socket | int]: ... -def Client(address: _Address, family: str | None = None, authkey: bytes | None = None) -> Connection: ... + object_list: Iterable[Connection[_SendT, _RecvT] | socket.socket | int], timeout: float | None = None +) -> list[Connection[_SendT, _RecvT] | socket.socket | int]: ... +def Client(address: _Address, family: str | None = None, authkey: bytes | None = None) -> Connection[Any, Any]: ... # N.B. Keep this in sync with multiprocessing.context.BaseContext.Pipe. # _ConnectionBase is the common base class of Connection and PipeConnection # and can be used in cross-platform code. +# +# The two connections should have the same generic types but inverted (Connection[_T1, _T2], Connection[_T2, _T1]). +# However, TypeVars scoped entirely within a return annotation is unspecified in the spec. if sys.platform != "win32": - def Pipe(duplex: bool = True) -> tuple[Connection, Connection]: ... + def Pipe(duplex: bool = True) -> tuple[Connection[Any, Any], Connection[Any, Any]]: ... else: - def Pipe(duplex: bool = True) -> tuple[PipeConnection, PipeConnection]: ... + def Pipe(duplex: bool = True) -> tuple[PipeConnection[Any, Any], PipeConnection[Any, Any]]: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index 605be4686c1ff..03d1d2e5c2203 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -1,7 +1,8 @@ import ctypes import sys +from _ctypes import _CData from collections.abc import Callable, Iterable, Sequence -from ctypes import _CData, _SimpleCData, c_char +from ctypes import _SimpleCData, c_char from logging import Logger, _Level as _LoggingLevel from multiprocessing import popen_fork, popen_forkserver, popen_spawn_posix, popen_spawn_win32, queues, synchronize from multiprocessing.managers import SyncManager @@ -47,10 +48,13 @@ class BaseContext: # N.B. Keep this in sync with multiprocessing.connection.Pipe. # _ConnectionBase is the common base class of Connection and PipeConnection # and can be used in cross-platform code. + # + # The two connections should have the same generic types but inverted (Connection[_T1, _T2], Connection[_T2, _T1]). + # However, TypeVars scoped entirely within a return annotation is unspecified in the spec. if sys.platform != "win32": - def Pipe(self, duplex: bool = True) -> tuple[Connection, Connection]: ... + def Pipe(self, duplex: bool = True) -> tuple[Connection[Any, Any], Connection[Any, Any]]: ... else: - def Pipe(self, duplex: bool = True) -> tuple[PipeConnection, PipeConnection]: ... + def Pipe(self, duplex: bool = True) -> tuple[PipeConnection[Any, Any], PipeConnection[Any, Any]]: ... def Barrier( self, parties: int, action: Callable[..., object] | None = None, timeout: float | None = None diff --git a/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi b/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi index 9a15f2683b7d4..31b9828563554 100644 --- a/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/forkserver.pyi @@ -1,12 +1,12 @@ from _typeshed import FileDescriptorLike, Unused from collections.abc import Sequence from struct import Struct -from typing import Any +from typing import Any, Final __all__ = ["ensure_running", "get_inherited_fds", "connect_to_new_process", "set_forkserver_preload"] -MAXFDS_TO_SEND: int -SIGNED_STRUCT: Struct +MAXFDS_TO_SEND: Final = 256 +SIGNED_STRUCT: Final[Struct] class ForkServer: def set_forkserver_preload(self, modules_names: list[str]) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 5d5b9cdcb9135..1669c5f09f978 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -1,7 +1,7 @@ import queue import sys import threading -from _typeshed import SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT +from _typeshed import Incomplete, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Sequence from types import TracebackType from typing import Any, AnyStr, ClassVar, Generic, SupportsIndex, TypeVar, overload @@ -10,6 +10,7 @@ from typing_extensions import Self, TypeAlias from .connection import Connection from .context import BaseContext from .shared_memory import _SLT, ShareableList as _ShareableList, SharedMemory as _SharedMemory +from .util import Finalize as _Finalize __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token", "SharedMemoryManager"] @@ -60,31 +61,58 @@ class ValueProxy(BaseProxy, Generic[_T]): if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): - __builtins__: ClassVar[dict[str, Any]] - def __len__(self) -> int: ... - def __getitem__(self, key: _KT, /) -> _VT: ... - def __setitem__(self, key: _KT, value: _VT, /) -> None: ... - def __delitem__(self, key: _KT, /) -> None: ... - def __iter__(self) -> Iterator[_KT]: ... - def copy(self) -> dict[_KT, _VT]: ... - @overload # type: ignore[override] - def get(self, key: _KT, /) -> _VT | None: ... - @overload - def get(self, key: _KT, default: _VT, /) -> _VT: ... - @overload - def get(self, key: _KT, default: _T, /) -> _VT | _T: ... - @overload - def pop(self, key: _KT, /) -> _VT: ... - @overload - def pop(self, key: _KT, default: _VT, /) -> _VT: ... - @overload - def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... - def keys(self) -> list[_KT]: ... # type: ignore[override] - def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] - def values(self) -> list[_VT]: ... # type: ignore[override] - if sys.version_info >= (3, 13): - def __class_getitem__(cls, args: Any, /) -> Any: ... +if sys.version_info >= (3, 13): + class _BaseDictProxy(BaseProxy, MutableMapping[_KT, _VT]): + __builtins__: ClassVar[dict[str, Any]] + def __len__(self) -> int: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... + def __delitem__(self, key: _KT, /) -> None: ... + def __iter__(self) -> Iterator[_KT]: ... + def copy(self) -> dict[_KT, _VT]: ... + @overload # type: ignore[override] + def get(self, key: _KT, /) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... + @overload + def pop(self, key: _KT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... + def keys(self) -> list[_KT]: ... # type: ignore[override] + def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] + def values(self) -> list[_VT]: ... # type: ignore[override] + + class DictProxy(_BaseDictProxy[_KT, _VT]): + def __class_getitem__(cls, args: Any, /) -> GenericAlias: ... + +else: + class DictProxy(BaseProxy, MutableMapping[_KT, _VT]): + __builtins__: ClassVar[dict[str, Any]] + def __len__(self) -> int: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... + def __delitem__(self, key: _KT, /) -> None: ... + def __iter__(self) -> Iterator[_KT]: ... + def copy(self) -> dict[_KT, _VT]: ... + @overload # type: ignore[override] + def get(self, key: _KT, /) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... + @overload + def pop(self, key: _KT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def pop(self, key: _KT, default: _T, /) -> _VT | _T: ... + def keys(self) -> list[_KT]: ... # type: ignore[override] + def items(self) -> list[tuple[_KT, _VT]]: ... # type: ignore[override] + def values(self) -> list[_VT]: ... # type: ignore[override] class BaseListProxy(BaseProxy, MutableSequence[_T]): __builtins__: ClassVar[dict[str, Any]] @@ -129,7 +157,9 @@ class Server: self, registry: dict[str, tuple[Callable[..., Any], Any, Any, Any]], address: Any, authkey: bytes, serializer: str ) -> None: ... def serve_forever(self) -> None: ... - def accept_connection(self, c: Connection, name: str) -> None: ... + def accept_connection( + self, c: Connection[tuple[str, str | None], tuple[str, str, Iterable[Incomplete], Mapping[str, Incomplete]]], name: str + ) -> None: ... class BaseManager: if sys.version_info >= (3, 11): @@ -154,7 +184,7 @@ class BaseManager: def get_server(self) -> Server: ... def connect(self) -> None: ... def start(self, initializer: Callable[..., object] | None = None, initargs: Iterable[Any] = ()) -> None: ... - def shutdown(self) -> None: ... # only available after start() was called + shutdown: _Finalize # only available after start() was called def join(self, timeout: float | None = None) -> None: ... # undocumented @property def address(self) -> Any: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index d2d611e3ca622..950ed1d8c56b6 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Iterable, Iterator, Mapping from types import TracebackType -from typing import Any, Generic, Literal, TypeVar +from typing import Any, Final, Generic, TypeVar from typing_extensions import Self if sys.version_info >= (3, 9): @@ -97,7 +97,7 @@ class ThreadPool(Pool): ) -> None: ... # undocumented -INIT: Literal["INIT"] -RUN: Literal["RUN"] -CLOSE: Literal["CLOSE"] -TERMINATE: Literal["TERMINATE"] +INIT: Final = "INIT" +RUN: Final = "RUN" +CLOSE: Final = "CLOSE" +TERMINATE: Final = "TERMINATE" diff --git a/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi b/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi index 3dc9d5bd7332a..481b9eec5a37c 100644 --- a/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/popen_spawn_win32.pyi @@ -1,16 +1,16 @@ import sys from multiprocessing.process import BaseProcess -from typing import ClassVar +from typing import ClassVar, Final from .util import Finalize if sys.platform == "win32": __all__ = ["Popen"] - TERMINATE: int - WINEXE: bool - WINSERVICE: bool - WINENV: bool + TERMINATE: Final[int] + WINEXE: Final[bool] + WINSERVICE: Final[bool] + WINENV: Final[bool] class Popen: finalizer: Finalize diff --git a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi index 91532633e1b9f..473e90936d71a 100644 --- a/mypy/typeshed/stdlib/multiprocessing/reduction.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/reduction.pyi @@ -8,14 +8,14 @@ from copyreg import _DispatchTableType from multiprocessing import connection from pickle import _ReducedType from socket import socket -from typing import Any, Literal +from typing import Any, Final if sys.platform == "win32": __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupHandle", "duplicate", "steal_handle"] else: __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupFd", "sendfds", "recvfds"] -HAVE_SEND_HANDLE: bool +HAVE_SEND_HANDLE: Final[bool] class ForkingPickler(pickle.Pickler): dispatch_table: _DispatchTableType @@ -35,18 +35,15 @@ if sys.platform == "win32": handle: int, target_process: int | None = None, inheritable: bool = False, *, source_process: int | None = None ) -> int: ... def steal_handle(source_pid: int, handle: int) -> int: ... - def send_handle(conn: connection.PipeConnection, handle: int, destination_pid: int) -> None: ... - def recv_handle(conn: connection.PipeConnection) -> int: ... + def send_handle(conn: connection.PipeConnection[DupHandle, Any], handle: int, destination_pid: int) -> None: ... + def recv_handle(conn: connection.PipeConnection[Any, DupHandle]) -> int: ... class DupHandle: def __init__(self, handle: int, access: int, pid: int | None = None) -> None: ... def detach(self) -> int: ... else: - if sys.platform == "darwin": - ACKNOWLEDGE: Literal[True] - else: - ACKNOWLEDGE: Literal[False] + ACKNOWLEDGE: Final[bool] def recvfds(sock: socket, size: int) -> list[int]: ... def send_handle(conn: HasFileno, handle: int, destination_pid: Unused) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi index 2b96ff0474706..5283445d8545b 100644 --- a/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/sharedctypes.pyi @@ -1,6 +1,7 @@ import ctypes +from _ctypes import _CData from collections.abc import Callable, Iterable, Sequence -from ctypes import _CData, _SimpleCData, c_char +from ctypes import _SimpleCData, c_char from multiprocessing.context import BaseContext from multiprocessing.synchronize import _LockLike from types import TracebackType @@ -73,7 +74,7 @@ def copy(obj: _CT) -> _CT: ... @overload def synchronized(obj: _SimpleCData[_T], lock: _LockLike | None = None, ctx: Any | None = None) -> Synchronized[_T]: ... @overload -def synchronized(obj: ctypes.Array[c_char], lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedString: ... # type: ignore +def synchronized(obj: ctypes.Array[c_char], lock: _LockLike | None = None, ctx: Any | None = None) -> SynchronizedString: ... @overload def synchronized( obj: ctypes.Array[_SimpleCData[_T]], lock: _LockLike | None = None, ctx: Any | None = None @@ -115,12 +116,12 @@ class SynchronizedArray(SynchronizedBase[ctypes.Array[_SimpleCData[_T]]], Generi class SynchronizedString(SynchronizedArray[bytes]): @overload # type: ignore[override] def __getitem__(self, i: slice) -> bytes: ... - @overload # type: ignore[override] + @overload def __getitem__(self, i: int) -> bytes: ... @overload # type: ignore[override] def __setitem__(self, i: slice, value: bytes) -> None: ... - @overload # type: ignore[override] - def __setitem__(self, i: int, value: bytes) -> None: ... # type: ignore[override] + @overload + def __setitem__(self, i: int, value: bytes) -> None: ... def __getslice__(self, start: int, stop: int) -> bytes: ... # type: ignore[override] def __setslice__(self, start: int, stop: int, values: bytes) -> None: ... # type: ignore[override] diff --git a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi index 26ff165756bfa..4a97532228974 100644 --- a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi @@ -1,6 +1,6 @@ from collections.abc import Mapping, Sequence from types import ModuleType -from typing import Any +from typing import Any, Final __all__ = [ "_main", @@ -12,8 +12,8 @@ __all__ = [ "import_main_path", ] -WINEXE: bool -WINSERVICE: bool +WINEXE: Final[bool] +WINSERVICE: Final[bool] def set_executable(exe: str) -> None: ... def get_executable() -> str: ... @@ -23,7 +23,7 @@ def get_command_line(**kwds: Any) -> list[str]: ... def spawn_main(pipe_handle: int, parent_pid: int | None = None, tracker_fd: int | None = None) -> None: ... # undocumented -def _main(fd: int) -> Any: ... +def _main(fd: int, parent_sentinel: int) -> int: ... def get_preparation_data(name: str) -> dict[str, Any]: ... old_main_modules: list[ModuleType] diff --git a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi index b417925fb17bc..e3cbfbc0ec826 100644 --- a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi @@ -1,6 +1,5 @@ import threading from collections.abc import Callable -from contextlib import AbstractContextManager from multiprocessing.context import BaseContext from types import TracebackType from typing_extensions import TypeAlias @@ -11,20 +10,23 @@ _LockLike: TypeAlias = Lock | RLock class Barrier(threading.Barrier): def __init__( - self, parties: int, action: Callable[[], object] | None = None, timeout: float | None = None, *ctx: BaseContext + self, parties: int, action: Callable[[], object] | None = None, timeout: float | None = None, *, ctx: BaseContext ) -> None: ... -class Condition(AbstractContextManager[bool, None]): +class Condition: def __init__(self, lock: _LockLike | None = None, *, ctx: BaseContext) -> None: ... def notify(self, n: int = 1) -> None: ... def notify_all(self) -> None: ... def wait(self, timeout: float | None = None) -> bool: ... def wait_for(self, predicate: Callable[[], bool], timeout: float | None = None) -> bool: ... - def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... - def release(self) -> None: ... + def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... + # These methods are copied from the lock passed to the constructor, or an + # instance of ctx.RLock() if lock was None. + def acquire(self, block: bool = True, timeout: float | None = None) -> bool: ... + def release(self) -> None: ... class Event: def __init__(self, *, ctx: BaseContext) -> None: ... @@ -34,12 +36,15 @@ class Event: def wait(self, timeout: float | None = None) -> bool: ... # Not part of public API -class SemLock(AbstractContextManager[bool, None]): - def acquire(self, block: bool = ..., timeout: float | None = ...) -> bool: ... - def release(self) -> None: ... +class SemLock: + def __init__(self, kind: int, value: int, maxvalue: int, *, ctx: BaseContext | None) -> None: ... + def __enter__(self) -> bool: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, / ) -> None: ... + # These methods are copied from the wrapped _multiprocessing.SemLock object + def acquire(self, block: bool = True, timeout: float | None = None) -> bool: ... + def release(self) -> None: ... class Lock(SemLock): def __init__(self, *, ctx: BaseContext) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/util.pyi b/mypy/typeshed/stdlib/multiprocessing/util.pyi index 790d6c7467f05..d5b6384afd5ed 100644 --- a/mypy/typeshed/stdlib/multiprocessing/util.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/util.pyi @@ -2,7 +2,7 @@ import threading from _typeshed import ConvertibleToInt, Incomplete, Unused from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence from logging import Logger, _Level as _LoggingLevel -from typing import Any, Generic, TypeVar, overload +from typing import Any, Final, Generic, TypeVar, overload __all__ = [ "sub_debug", @@ -25,14 +25,14 @@ __all__ = [ _T = TypeVar("_T") _R_co = TypeVar("_R_co", default=Any, covariant=True) -NOTSET: int -SUBDEBUG: int -DEBUG: int -INFO: int -SUBWARNING: int +NOTSET: Final[int] +SUBDEBUG: Final[int] +DEBUG: Final[int] +INFO: Final[int] +SUBWARNING: Final[int] -LOGGER_NAME: str -DEFAULT_LOGGING_FORMAT: str +LOGGER_NAME: Final[str] +DEFAULT_LOGGING_FORMAT: Final[str] def sub_debug(msg: object, *args: object) -> None: ... def debug(msg: object, *args: object) -> None: ... @@ -92,7 +92,7 @@ class ForkAwareThreadLock: class ForkAwareLocal(threading.local): ... -MAXFD: int +MAXFD: Final[int] def close_all_fds_except(fds: Iterable[int]) -> None: ... def spawnv_passfds(path: bytes, args: Sequence[ConvertibleToInt], passfds: Sequence[int]) -> int: ... diff --git a/mypy/typeshed/stdlib/nntplib.pyi b/mypy/typeshed/stdlib/nntplib.pyi index 969c657e9aab8..85dfbff1cb50e 100644 --- a/mypy/typeshed/stdlib/nntplib.pyi +++ b/mypy/typeshed/stdlib/nntplib.pyi @@ -5,7 +5,7 @@ import sys from _typeshed import Unused from builtins import list as _list # conflicts with a method named "list" from collections.abc import Iterable -from typing import IO, Any, Literal, NamedTuple +from typing import IO, Any, Final, NamedTuple from typing_extensions import Self, TypeAlias __all__ = [ @@ -31,8 +31,8 @@ class NNTPPermanentError(NNTPError): ... class NNTPProtocolError(NNTPError): ... class NNTPDataError(NNTPError): ... -NNTP_PORT: Literal[119] -NNTP_SSL_PORT: Literal[563] +NNTP_PORT: Final = 119 +NNTP_SSL_PORT: Final = 563 class GroupInfo(NamedTuple): group: str diff --git a/mypy/typeshed/stdlib/nt.pyi b/mypy/typeshed/stdlib/nt.pyi index 4066096f4c71b..e1d57d09a9bd5 100644 --- a/mypy/typeshed/stdlib/nt.pyi +++ b/mypy/typeshed/stdlib/nt.pyi @@ -107,5 +107,7 @@ if sys.platform == "win32": listvolumes as listvolumes, set_blocking as set_blocking, ) + if sys.version_info >= (3, 13): + from os import fchmod as fchmod, lchmod as lchmod environ: dict[str, str] diff --git a/mypy/typeshed/stdlib/operator.pyi b/mypy/typeshed/stdlib/operator.pyi index a0e5df7977daf..b73e037f3ed9b 100644 --- a/mypy/typeshed/stdlib/operator.pyi +++ b/mypy/typeshed/stdlib/operator.pyi @@ -1,5 +1,66 @@ import sys -from _operator import * +from _operator import ( + abs as abs, + add as add, + and_ as and_, + concat as concat, + contains as contains, + countOf as countOf, + delitem as delitem, + eq as eq, + floordiv as floordiv, + ge as ge, + getitem as getitem, + gt as gt, + iadd as iadd, + iand as iand, + iconcat as iconcat, + ifloordiv as ifloordiv, + ilshift as ilshift, + imatmul as imatmul, + imod as imod, + imul as imul, + index as index, + indexOf as indexOf, + inv as inv, + invert as invert, + ior as ior, + ipow as ipow, + irshift as irshift, + is_ as is_, + is_not as is_not, + isub as isub, + itruediv as itruediv, + ixor as ixor, + le as le, + length_hint as length_hint, + lshift as lshift, + lt as lt, + matmul as matmul, + mod as mod, + mul as mul, + ne as ne, + neg as neg, + not_ as not_, + or_ as or_, + pos as pos, + pow as pow, + rshift as rshift, + setitem as setitem, + sub as sub, + truediv as truediv, + truth as truth, + xor as xor, +) +from _typeshed import SupportsGetItem +from typing import Any, Generic, TypeVar, final, overload +from typing_extensions import TypeVarTuple, Unpack + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_Ts = TypeVarTuple("_Ts") __all__ = [ "abs", @@ -59,8 +120,15 @@ __all__ = [ ] if sys.version_info >= (3, 11): + from _operator import call as call + __all__ += ["call"] +if sys.version_info >= (3, 14): + from _operator import is_none as is_none, is_not_none as is_not_none + + __all__ += ["is_none", "is_not_none"] + __lt__ = lt __le__ = le __eq__ = eq @@ -108,3 +176,40 @@ __itruediv__ = itruediv __ixor__ = ixor if sys.version_info >= (3, 11): __call__ = call + +# At runtime, these classes are implemented in C as part of the _operator module +# However, they consider themselves to live in the operator module, so we'll put +# them here. +@final +class attrgetter(Generic[_T_co]): + @overload + def __new__(cls, attr: str, /) -> attrgetter[Any]: ... + @overload + def __new__(cls, attr: str, attr2: str, /) -> attrgetter[tuple[Any, Any]]: ... + @overload + def __new__(cls, attr: str, attr2: str, attr3: str, /) -> attrgetter[tuple[Any, Any, Any]]: ... + @overload + def __new__(cls, attr: str, attr2: str, attr3: str, attr4: str, /) -> attrgetter[tuple[Any, Any, Any, Any]]: ... + @overload + def __new__(cls, attr: str, /, *attrs: str) -> attrgetter[tuple[Any, ...]]: ... + def __call__(self, obj: Any, /) -> _T_co: ... + +@final +class itemgetter(Generic[_T_co]): + @overload + def __new__(cls, item: _T, /) -> itemgetter[_T]: ... + @overload + def __new__(cls, item1: _T1, item2: _T2, /, *items: Unpack[_Ts]) -> itemgetter[tuple[_T1, _T2, Unpack[_Ts]]]: ... + # __key: _KT_contra in SupportsGetItem seems to be causing variance issues, ie: + # TypeVar "_KT_contra@SupportsGetItem" is contravariant + # "tuple[int, int]" is incompatible with protocol "SupportsIndex" + # preventing [_T_co, ...] instead of [Any, ...] + # + # A suspected mypy issue prevents using [..., _T] instead of [..., Any] here. + # https://github.com/python/mypy/issues/14032 + def __call__(self, obj: SupportsGetItem[Any, Any]) -> Any: ... + +@final +class methodcaller: + def __init__(self, name: str, /, *args: Any, **kwargs: Any) -> None: ... + def __call__(self, obj: Any) -> Any: ... diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index a179c2d1bb3ce..d6db7a06f291a 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,7 +1,7 @@ -from _typeshed import Incomplete +from _typeshed import Incomplete, MaybeNone from abc import abstractmethod from collections.abc import Callable, Iterable, Mapping, Sequence -from typing import IO, Any, AnyStr, Literal, overload +from typing import IO, Any, AnyStr, Literal, NoReturn, overload __all__ = [ "Option", @@ -56,7 +56,7 @@ class HelpFormatter: current_indent: int default_tag: str help_position: int - help_width: int | Any # initialized as None and computed later as int when storing option strings + help_width: int | MaybeNone # initialized as None and computed later as int when storing option strings indent_increment: int level: int max_help_position: int @@ -231,8 +231,8 @@ class OptionParser(OptionContainer): def check_values(self, values: Values, args: list[str]) -> tuple[Values, list[str]]: ... def disable_interspersed_args(self) -> None: ... def enable_interspersed_args(self) -> None: ... - def error(self, msg: str) -> None: ... - def exit(self, status: int = 0, msg: str | None = None) -> None: ... + def error(self, msg: str) -> NoReturn: ... + def exit(self, status: int = 0, msg: str | None = None) -> NoReturn: ... def expand_prog_name(self, s: str) -> str: ... def format_epilog(self, formatter: HelpFormatter) -> str: ... def format_help(self, formatter: HelpFormatter | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 9b00117a55999..98260b14e7ed4 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -19,12 +19,12 @@ from _typeshed import ( WriteableBuffer, structseq, ) -from abc import abstractmethod +from abc import ABC, abstractmethod from builtins import OSError from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, Sequence -from contextlib import AbstractContextManager -from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper as _TextIOWrapper +from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from subprocess import Popen +from types import TracebackType from typing import ( IO, Any, @@ -47,6 +47,446 @@ from . import path as _path if sys.version_info >= (3, 9): from types import GenericAlias +__all__ = [ + "F_OK", + "O_APPEND", + "O_CREAT", + "O_EXCL", + "O_RDONLY", + "O_RDWR", + "O_TRUNC", + "O_WRONLY", + "P_NOWAIT", + "P_NOWAITO", + "P_WAIT", + "R_OK", + "SEEK_CUR", + "SEEK_END", + "SEEK_SET", + "TMP_MAX", + "W_OK", + "X_OK", + "DirEntry", + "_exit", + "abort", + "access", + "altsep", + "chdir", + "chmod", + "close", + "closerange", + "cpu_count", + "curdir", + "defpath", + "device_encoding", + "devnull", + "dup", + "dup2", + "environ", + "error", + "execl", + "execle", + "execlp", + "execlpe", + "execv", + "execve", + "execvp", + "execvpe", + "extsep", + "fdopen", + "fsdecode", + "fsencode", + "fspath", + "fstat", + "fsync", + "ftruncate", + "get_exec_path", + "get_inheritable", + "get_terminal_size", + "getcwd", + "getcwdb", + "getenv", + "getlogin", + "getpid", + "getppid", + "isatty", + "kill", + "linesep", + "link", + "listdir", + "lseek", + "lstat", + "makedirs", + "mkdir", + "name", + "open", + "pardir", + "path", + "pathsep", + "pipe", + "popen", + "putenv", + "read", + "readlink", + "remove", + "removedirs", + "rename", + "renames", + "replace", + "rmdir", + "scandir", + "sep", + "set_inheritable", + "spawnl", + "spawnle", + "spawnv", + "spawnve", + "stat", + "stat_result", + "statvfs_result", + "strerror", + "supports_bytes_environ", + "symlink", + "system", + "terminal_size", + "times", + "times_result", + "truncate", + "umask", + "uname_result", + "unlink", + "urandom", + "utime", + "waitpid", + "walk", + "write", +] +if sys.version_info >= (3, 9): + __all__ += ["waitstatus_to_exitcode"] +if sys.platform == "darwin" and sys.version_info >= (3, 12): + __all__ += ["PRIO_DARWIN_BG", "PRIO_DARWIN_NONUI", "PRIO_DARWIN_PROCESS", "PRIO_DARWIN_THREAD"] +if sys.platform == "darwin" and sys.version_info >= (3, 10): + __all__ += ["O_EVTONLY", "O_NOFOLLOW_ANY", "O_SYMLINK"] +if sys.platform == "linux": + __all__ += [ + "GRND_NONBLOCK", + "GRND_RANDOM", + "MFD_ALLOW_SEALING", + "MFD_CLOEXEC", + "MFD_HUGETLB", + "MFD_HUGE_16GB", + "MFD_HUGE_16MB", + "MFD_HUGE_1GB", + "MFD_HUGE_1MB", + "MFD_HUGE_256MB", + "MFD_HUGE_2GB", + "MFD_HUGE_2MB", + "MFD_HUGE_32MB", + "MFD_HUGE_512KB", + "MFD_HUGE_512MB", + "MFD_HUGE_64KB", + "MFD_HUGE_8MB", + "MFD_HUGE_MASK", + "MFD_HUGE_SHIFT", + "O_DIRECT", + "O_LARGEFILE", + "O_NOATIME", + "O_PATH", + "O_RSYNC", + "O_TMPFILE", + "RTLD_DEEPBIND", + "SCHED_BATCH", + "SCHED_IDLE", + "SCHED_RESET_ON_FORK", + "XATTR_CREATE", + "XATTR_REPLACE", + "XATTR_SIZE_MAX", + "copy_file_range", + "getrandom", + "getxattr", + "listxattr", + "memfd_create", + "removexattr", + "setxattr", + ] +if sys.platform == "linux" and sys.version_info >= (3, 13): + __all__ += [ + "POSIX_SPAWN_CLOSEFROM", + "TFD_CLOEXEC", + "TFD_NONBLOCK", + "TFD_TIMER_ABSTIME", + "TFD_TIMER_CANCEL_ON_SET", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime_ns", + "timerfd_settime", + "timerfd_settime_ns", + ] +if sys.platform == "linux" and sys.version_info >= (3, 12): + __all__ += [ + "CLONE_FILES", + "CLONE_FS", + "CLONE_NEWCGROUP", + "CLONE_NEWIPC", + "CLONE_NEWNET", + "CLONE_NEWNS", + "CLONE_NEWPID", + "CLONE_NEWUSER", + "CLONE_NEWUTS", + "CLONE_SIGHAND", + "CLONE_SYSVSEM", + "CLONE_THREAD", + "CLONE_VM", + "setns", + "unshare", + ] +if sys.platform == "linux" and sys.version_info >= (3, 10): + __all__ += [ + "EFD_CLOEXEC", + "EFD_NONBLOCK", + "EFD_SEMAPHORE", + "RWF_APPEND", + "SPLICE_F_MORE", + "SPLICE_F_MOVE", + "SPLICE_F_NONBLOCK", + "eventfd", + "eventfd_read", + "eventfd_write", + "splice", + ] +if sys.platform == "linux" and sys.version_info >= (3, 9): + __all__ += ["P_PIDFD", "pidfd_open"] +if sys.platform == "win32": + __all__ += [ + "O_BINARY", + "O_NOINHERIT", + "O_RANDOM", + "O_SEQUENTIAL", + "O_SHORT_LIVED", + "O_TEMPORARY", + "O_TEXT", + "P_DETACH", + "P_OVERLAY", + "get_handle_inheritable", + "set_handle_inheritable", + "startfile", + ] +if sys.platform == "win32" and sys.version_info >= (3, 12): + __all__ += ["listdrives", "listmounts", "listvolumes"] +if sys.platform != "win32": + __all__ += [ + "CLD_CONTINUED", + "CLD_DUMPED", + "CLD_EXITED", + "CLD_TRAPPED", + "EX_CANTCREAT", + "EX_CONFIG", + "EX_DATAERR", + "EX_IOERR", + "EX_NOHOST", + "EX_NOINPUT", + "EX_NOPERM", + "EX_NOUSER", + "EX_OSERR", + "EX_OSFILE", + "EX_PROTOCOL", + "EX_SOFTWARE", + "EX_TEMPFAIL", + "EX_UNAVAILABLE", + "EX_USAGE", + "F_LOCK", + "F_TEST", + "F_TLOCK", + "F_ULOCK", + "NGROUPS_MAX", + "O_ACCMODE", + "O_ASYNC", + "O_CLOEXEC", + "O_DIRECTORY", + "O_DSYNC", + "O_NDELAY", + "O_NOCTTY", + "O_NOFOLLOW", + "O_NONBLOCK", + "O_SYNC", + "POSIX_SPAWN_CLOSE", + "POSIX_SPAWN_DUP2", + "POSIX_SPAWN_OPEN", + "PRIO_PGRP", + "PRIO_PROCESS", + "PRIO_USER", + "P_ALL", + "P_PGID", + "P_PID", + "RTLD_GLOBAL", + "RTLD_LAZY", + "RTLD_LOCAL", + "RTLD_NODELETE", + "RTLD_NOLOAD", + "RTLD_NOW", + "SCHED_FIFO", + "SCHED_OTHER", + "SCHED_RR", + "SEEK_DATA", + "SEEK_HOLE", + "ST_NOSUID", + "ST_RDONLY", + "WCONTINUED", + "WCOREDUMP", + "WEXITED", + "WEXITSTATUS", + "WIFCONTINUED", + "WIFEXITED", + "WIFSIGNALED", + "WIFSTOPPED", + "WNOHANG", + "WNOWAIT", + "WSTOPPED", + "WSTOPSIG", + "WTERMSIG", + "WUNTRACED", + "chown", + "chroot", + "confstr", + "confstr_names", + "ctermid", + "environb", + "fchdir", + "fchown", + "fork", + "forkpty", + "fpathconf", + "fstatvfs", + "fwalk", + "getegid", + "getenvb", + "geteuid", + "getgid", + "getgrouplist", + "getgroups", + "getloadavg", + "getpgid", + "getpgrp", + "getpriority", + "getsid", + "getuid", + "initgroups", + "killpg", + "lchown", + "lockf", + "major", + "makedev", + "minor", + "mkfifo", + "mknod", + "nice", + "openpty", + "pathconf", + "pathconf_names", + "posix_spawn", + "posix_spawnp", + "pread", + "preadv", + "pwrite", + "pwritev", + "readv", + "register_at_fork", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_yield", + "sendfile", + "setegid", + "seteuid", + "setgid", + "setgroups", + "setpgid", + "setpgrp", + "setpriority", + "setregid", + "setreuid", + "setsid", + "setuid", + "spawnlp", + "spawnlpe", + "spawnvp", + "spawnvpe", + "statvfs", + "sync", + "sysconf", + "sysconf_names", + "tcgetpgrp", + "tcsetpgrp", + "ttyname", + "uname", + "wait", + "wait3", + "wait4", + "writev", + ] +if sys.platform != "win32" and sys.version_info >= (3, 13): + __all__ += ["grantpt", "posix_openpt", "ptsname", "unlockpt"] +if sys.platform != "win32" and sys.version_info >= (3, 11): + __all__ += ["login_tty"] +if sys.platform != "win32" and sys.version_info >= (3, 10): + __all__ += ["O_FSYNC"] +if sys.platform != "win32" and sys.version_info >= (3, 9): + __all__ += ["CLD_KILLED", "CLD_STOPPED"] +if sys.platform != "darwin" and sys.platform != "win32": + __all__ += [ + "POSIX_FADV_DONTNEED", + "POSIX_FADV_NOREUSE", + "POSIX_FADV_NORMAL", + "POSIX_FADV_RANDOM", + "POSIX_FADV_SEQUENTIAL", + "POSIX_FADV_WILLNEED", + "RWF_DSYNC", + "RWF_HIPRI", + "RWF_NOWAIT", + "RWF_SYNC", + "ST_APPEND", + "ST_MANDLOCK", + "ST_NOATIME", + "ST_NODEV", + "ST_NODIRATIME", + "ST_NOEXEC", + "ST_RELATIME", + "ST_SYNCHRONOUS", + "ST_WRITE", + "fdatasync", + "getresgid", + "getresuid", + "pipe2", + "posix_fadvise", + "posix_fallocate", + "sched_getaffinity", + "sched_getparam", + "sched_getscheduler", + "sched_param", + "sched_rr_get_interval", + "sched_setaffinity", + "sched_setparam", + "sched_setscheduler", + "setresgid", + "setresuid", + ] +if sys.platform != "linux" and sys.platform != "win32": + __all__ += ["O_EXLOCK", "O_SHLOCK", "chflags", "lchflags"] +if sys.platform != "linux" and sys.platform != "win32" and sys.version_info >= (3, 13): + __all__ += ["O_EXEC", "O_SEARCH"] +if sys.platform != "darwin" or sys.version_info >= (3, 13): + if sys.platform != "win32": + __all__ += ["waitid", "waitid_result"] +if sys.platform != "win32" or sys.version_info >= (3, 13): + __all__ += ["fchmod"] + if sys.platform != "linux": + __all__ += ["lchmod"] +if sys.platform != "win32" or sys.version_info >= (3, 12): + __all__ += ["get_blocking", "set_blocking"] +if sys.platform != "win32" or sys.version_info >= (3, 11): + __all__ += ["EX_OK"] +if sys.platform != "win32" or sys.version_info >= (3, 9): + __all__ += ["unsetenv"] + # This unnecessary alias is to work around various errors path = _path @@ -124,15 +564,16 @@ if sys.platform != "win32": CLD_KILLED: int CLD_STOPPED: int - # TODO: SCHED_RESET_ON_FORK not available on darwin? - # TODO: SCHED_BATCH and SCHED_IDLE are linux only? - SCHED_OTHER: int # some flavors of Unix - SCHED_BATCH: int # some flavors of Unix - SCHED_IDLE: int # some flavors of Unix - SCHED_SPORADIC: int # some flavors of Unix - SCHED_FIFO: int # some flavors of Unix - SCHED_RR: int # some flavors of Unix - SCHED_RESET_ON_FORK: int # some flavors of Unix + SCHED_OTHER: int + SCHED_FIFO: int + SCHED_RR: int + if sys.platform != "darwin" and sys.platform != "linux": + SCHED_SPORADIC: int + +if sys.platform == "linux": + SCHED_BATCH: int + SCHED_IDLE: int + SCHED_RESET_ON_FORK: int if sys.platform != "win32": RTLD_LAZY: int @@ -157,8 +598,8 @@ SEEK_SET: int SEEK_CUR: int SEEK_END: int if sys.platform != "win32": - SEEK_DATA: int # some flavors of Unix - SEEK_HOLE: int # some flavors of Unix + SEEK_DATA: int + SEEK_HOLE: int O_RDONLY: int O_WRONLY: int @@ -167,34 +608,50 @@ O_APPEND: int O_CREAT: int O_EXCL: int O_TRUNC: int -# We don't use sys.platform for O_* flags to denote platform-dependent APIs because some codes, -# including tests for mypy, use a more finer way than sys.platform before using these APIs -# See https://github.com/python/typeshed/pull/2286 for discussions -O_DSYNC: int # Unix only -O_RSYNC: int # Unix only -O_SYNC: int # Unix only -O_NDELAY: int # Unix only -O_NONBLOCK: int # Unix only -O_NOCTTY: int # Unix only -O_CLOEXEC: int # Unix only -O_SHLOCK: int # Unix only -O_EXLOCK: int # Unix only -O_BINARY: int # Windows only -O_NOINHERIT: int # Windows only -O_SHORT_LIVED: int # Windows only -O_TEMPORARY: int # Windows only -O_RANDOM: int # Windows only -O_SEQUENTIAL: int # Windows only -O_TEXT: int # Windows only -O_ASYNC: int # Gnu extension if in C library -O_DIRECT: int # Gnu extension if in C library -O_DIRECTORY: int # Gnu extension if in C library -O_NOFOLLOW: int # Gnu extension if in C library -O_NOATIME: int # Gnu extension if in C library -O_PATH: int # Gnu extension if in C library -O_TMPFILE: int # Gnu extension if in C library -O_LARGEFILE: int # Gnu extension if in C library -O_ACCMODE: int # TODO: when does this exist? +if sys.platform == "win32": + O_BINARY: int + O_NOINHERIT: int + O_SHORT_LIVED: int + O_TEMPORARY: int + O_RANDOM: int + O_SEQUENTIAL: int + O_TEXT: int + +if sys.platform != "win32": + O_DSYNC: int + O_SYNC: int + O_NDELAY: int + O_NONBLOCK: int + O_NOCTTY: int + O_CLOEXEC: int + O_ASYNC: int # Gnu extension if in C library + O_DIRECTORY: int # Gnu extension if in C library + O_NOFOLLOW: int # Gnu extension if in C library + O_ACCMODE: int # TODO: when does this exist? + +if sys.platform == "linux": + O_RSYNC: int + O_DIRECT: int # Gnu extension if in C library + O_NOATIME: int # Gnu extension if in C library + O_PATH: int # Gnu extension if in C library + O_TMPFILE: int # Gnu extension if in C library + O_LARGEFILE: int # Gnu extension if in C library + +if sys.platform != "linux" and sys.platform != "win32": + O_SHLOCK: int + O_EXLOCK: int + +if sys.platform == "darwin" and sys.version_info >= (3, 10): + O_EVTONLY: int + O_NOFOLLOW_ANY: int + O_SYMLINK: int + +if sys.platform != "win32" and sys.version_info >= (3, 10): + O_FSYNC: int + +if sys.platform != "linux" and sys.platform != "win32" and sys.version_info >= (3, 13): + O_EXEC: int + O_SEARCH: int if sys.platform != "win32" and sys.platform != "darwin": # posix, but apparently missing on macos @@ -365,7 +822,9 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo if sys.version_info >= (3, 12) and sys.platform == "win32": @property @deprecated( - "Use st_birthtime instead to retrieve the file creation time. In the future, this property will contain the last metadata change time." + """\ +Use st_birthtime instead to retrieve the file creation time. \ +In the future, this property will contain the last metadata change time.""" ) def st_ctime(self) -> float: ... else: @@ -410,8 +869,11 @@ class stat_result(structseq[float], tuple[int, int, int, int, int, int, int, flo # Attributes documented as sometimes appearing, but deliberately omitted from the stub: `st_creator`, `st_rsize`, `st_type`. # See https://github.com/python/typeshed/pull/6560#issuecomment-991253327 +# mypy and pyright object to this being both ABC and Protocol. +# At runtime it inherits from ABC and is not a Protocol, but it will be +# on the allowlist for use as a Protocol starting in 3.14. @runtime_checkable -class PathLike(Protocol[AnyStr_co]): +class PathLike(ABC, Protocol[AnyStr_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] @abstractmethod def __fspath__(self) -> AnyStr_co: ... @@ -576,7 +1038,7 @@ def fdopen( newline: str | None = ..., closefd: bool = ..., opener: _Opener | None = ..., -) -> _TextIOWrapper: ... +) -> TextIOWrapper: ... @overload def fdopen( fd: int, @@ -671,7 +1133,6 @@ if sys.version_info >= (3, 12) or sys.platform != "win32": def set_blocking(fd: int, blocking: bool, /) -> None: ... if sys.platform != "win32": - def fchmod(fd: int, mode: int) -> None: ... def fchown(fd: int, uid: int, gid: int) -> None: ... def fpathconf(fd: int, name: str | int, /) -> int: ... def fstatvfs(fd: int, /) -> statvfs_result: ... @@ -752,7 +1213,6 @@ def chmod(path: FileDescriptorOrPath, mode: int, *, dir_fd: int | None = None, f if sys.platform != "win32" and sys.platform != "linux": def chflags(path: StrOrBytesPath, flags: int, follow_symlinks: bool = True) -> None: ... # some flavors of Unix def lchflags(path: StrOrBytesPath, flags: int) -> None: ... - def lchmod(path: StrOrBytesPath, mode: int) -> None: ... if sys.platform != "win32": def chroot(path: StrOrBytesPath) -> None: ... @@ -793,9 +1253,12 @@ def replace( src: StrOrBytesPath, dst: StrOrBytesPath, *, src_dir_fd: int | None = None, dst_dir_fd: int | None = None ) -> None: ... def rmdir(path: StrOrBytesPath, *, dir_fd: int | None = None) -> None: ... - -class _ScandirIterator(Iterator[DirEntry[AnyStr]], AbstractContextManager[_ScandirIterator[AnyStr], None]): +@final +class _ScandirIterator(Generic[AnyStr]): + def __del__(self) -> None: ... + def __iter__(self) -> Self: ... def __next__(self) -> DirEntry[AnyStr]: ... + def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... def close(self) -> None: ... @@ -917,9 +1380,25 @@ if sys.platform != "win32": if sys.platform != "darwin" and sys.platform != "linux": def plock(op: int, /) -> None: ... -class _wrap_close(_TextIOWrapper): - def __init__(self, stream: _TextIOWrapper, proc: Popen[str]) -> None: ... - def close(self) -> int | None: ... # type: ignore[override] +class _wrap_close: + def __init__(self, stream: TextIOWrapper, proc: Popen[str]) -> None: ... + def close(self) -> int | None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + def __iter__(self) -> Iterator[str]: ... + # Methods below here don't exist directly on the _wrap_close object, but + # are copied from the wrapped TextIOWrapper object via __getattr__. + # The full set of TextIOWrapper methods are technically available this way, + # but undocumented. Only a subset are currently included here. + def read(self, size: int | None = -1, /) -> str: ... + def readable(self) -> bool: ... + def readline(self, size: int = -1, /) -> str: ... + def readlines(self, hint: int = -1, /) -> list[str]: ... + def writable(self) -> bool: ... + def write(self, s: str, /) -> int: ... + def writelines(self, lines: Iterable[str], /) -> None: ... def popen(cmd: str, mode: str = "r", buffering: int = -1) -> _wrap_close: ... def spawnl(mode: int, file: StrOrBytesPath, arg0: StrOrBytesPath, *args: StrOrBytesPath) -> int: ... @@ -971,7 +1450,8 @@ else: def spawnvp(mode: int, file: StrOrBytesPath, args: _ExecVArgs) -> int: ... def spawnvpe(mode: int, file: StrOrBytesPath, args: _ExecVArgs, env: _ExecEnv) -> int: ... def wait() -> tuple[int, int]: ... # Unix only - if sys.platform != "darwin": + # Added to MacOS in 3.13 + if sys.platform != "darwin" or sys.version_info >= (3, 13): @final class waitid_result(structseq[int], tuple[int, int, int, int, int]): if sys.version_info >= (3, 10): @@ -1058,6 +1538,14 @@ if sys.platform != "win32": def cpu_count() -> int | None: ... +if sys.version_info >= (3, 13): + # Documented to return `int | None`, but falls back to `len(sched_getaffinity(0))` when + # available. See https://github.com/python/cpython/blob/417c130/Lib/os.py#L1175-L1186. + if sys.platform != "win32" and sys.platform != "darwin": + def process_cpu_count() -> int: ... + else: + def process_cpu_count() -> int | None: ... + if sys.platform != "win32": # Unix only def confstr(name: str | int, /) -> str | None: ... @@ -1155,3 +1643,33 @@ if sys.version_info >= (3, 12) and sys.platform == "linux": CLONE_VM: int def unshare(flags: int) -> None: ... def setns(fd: FileDescriptorLike, nstype: int = 0) -> None: ... + +if sys.version_info >= (3, 13) and sys.platform != "win32": + def posix_openpt(oflag: int, /) -> int: ... + def grantpt(fd: FileDescriptorLike, /) -> None: ... + def unlockpt(fd: FileDescriptorLike, /) -> None: ... + def ptsname(fd: FileDescriptorLike, /) -> str: ... + +if sys.version_info >= (3, 13) and sys.platform == "linux": + TFD_TIMER_ABSTIME: Final = 1 + TFD_TIMER_CANCEL_ON_SET: Final = 2 + TFD_NONBLOCK: Final[int] + TFD_CLOEXEC: Final[int] + POSIX_SPAWN_CLOSEFROM: Final[int] + + def timerfd_create(clockid: int, /, *, flags: int = 0) -> int: ... + def timerfd_settime( + fd: FileDescriptor, /, *, flags: int = 0, initial: float = 0.0, interval: float = 0.0 + ) -> tuple[float, float]: ... + def timerfd_settime_ns(fd: FileDescriptor, /, *, flags: int = 0, initial: int = 0, interval: int = 0) -> tuple[int, int]: ... + def timerfd_gettime(fd: FileDescriptor, /) -> tuple[float, float]: ... + def timerfd_gettime_ns(fd: FileDescriptor, /) -> tuple[int, int]: ... + +if sys.version_info >= (3, 13) or sys.platform != "win32": + # Added to Windows in 3.13. + def fchmod(fd: int, mode: int) -> None: ... + +if sys.platform != "linux": + if sys.version_info >= (3, 13) or sys.platform != "win32": + # Added to Windows in 3.13. + def lchmod(path: StrOrBytesPath, mode: int) -> None: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index c8c8dde0f33e3..bdca375f626da 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -49,7 +49,7 @@ class PurePath(PathLike[str]): def stem(self) -> str: ... if sys.version_info >= (3, 12): def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... - def __init__(self, *args: StrPath) -> None: ... + def __init__(self, *args: StrPath) -> None: ... # pyright: ignore[reportInconsistentConstructor] else: def __new__(cls, *args: StrPath) -> Self: ... @@ -101,7 +101,11 @@ class PurePosixPath(PurePath): ... class PureWindowsPath(PurePath): ... class Path(PurePath): - def __new__(cls, *args: StrPath, **kwargs: Any) -> Self: ... + if sys.version_info >= (3, 12): + def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... # pyright: ignore[reportInconsistentConstructor] + else: + def __new__(cls, *args: StrPath, **kwargs: Unused) -> Self: ... + @classmethod def cwd(cls) -> Self: ... if sys.version_info >= (3, 10): @@ -113,7 +117,7 @@ class Path(PurePath): if sys.version_info >= (3, 13): @classmethod - def from_uri(cls, uri: str) -> Path: ... + def from_uri(cls, uri: str) -> Self: ... def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... def is_file(self, *, follow_symlinks: bool = True) -> bool: ... def read_text(self, encoding: str | None = None, errors: str | None = None, newline: str | None = None) -> str: ... @@ -155,6 +159,20 @@ class Path(PurePath): def lchmod(self, mode: int) -> None: ... def lstat(self) -> stat_result: ... def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False) -> None: ... + + if sys.version_info >= (3, 14): + def copy(self, target: StrPath, *, follow_symlinks: bool = True, preserve_metadata: bool = False) -> None: ... + def copytree( + self, + target: StrPath, + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + dirs_exist_ok: bool = False, + ignore: Callable[[Self], bool] | None = None, + on_error: Callable[[OSError], object] | None = None, + ) -> None: ... + # Adapted from builtins.open # Text mode: always returns a TextIOWrapper # The Traversable .open in stdlib/importlib/abc.pyi should be kept in sync with this. @@ -228,10 +246,18 @@ class Path(PurePath): if sys.version_info >= (3, 9): def readlink(self) -> Self: ... - def rename(self, target: str | PurePath) -> Self: ... - def replace(self, target: str | PurePath) -> Self: ... + if sys.version_info >= (3, 10): + def rename(self, target: StrPath) -> Self: ... + def replace(self, target: StrPath) -> Self: ... + else: + def rename(self, target: str | PurePath) -> Self: ... + def replace(self, target: str | PurePath) -> Self: ... + def resolve(self, strict: bool = False) -> Self: ... def rmdir(self) -> None: ... + if sys.version_info >= (3, 14): + def delete(self, ignore_errors: bool = False, on_error: Callable[[OSError], object] | None = None) -> None: ... + def symlink_to(self, target: StrOrBytesPath, target_is_directory: bool = False) -> None: ... if sys.version_info >= (3, 10): def hardlink_to(self, target: StrOrBytesPath) -> None: ... @@ -262,6 +288,9 @@ class Path(PurePath): self, top_down: bool = ..., on_error: Callable[[OSError], object] | None = ..., follow_symlinks: bool = ... ) -> Iterator[tuple[Self, list[str], list[str]]]: ... + if sys.version_info >= (3, 14): + def rmtree(self, ignore_errors: bool = False, on_error: Callable[[OSError], object] | None = None) -> None: ... + class PosixPath(Path, PurePosixPath): ... class WindowsPath(Path, PureWindowsPath): ... diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index 487adddd04bf0..61e8b7176e849 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -5,7 +5,7 @@ from cmd import Cmd from collections.abc import Callable, Iterable, Mapping, Sequence from inspect import _SourceObjectType from types import CodeType, FrameType, TracebackType -from typing import IO, Any, ClassVar, TypeVar +from typing import IO, Any, ClassVar, Final, TypeVar from typing_extensions import ParamSpec, Self __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", "post_mortem", "help"] @@ -30,6 +30,9 @@ class Pdb(Bdb, Cmd): commands_resuming: ClassVar[list[str]] + if sys.version_info >= (3, 13): + MAX_CHAINED_EXCEPTION_DEPTH: Final = 999 + aliases: dict[str, str] mainpyfile: str _wait_for_mainpyfile: bool @@ -58,8 +61,16 @@ class Pdb(Bdb, Cmd): if sys.version_info < (3, 11): def execRcLines(self) -> None: ... + if sys.version_info >= (3, 13): + user_opcode = Bdb.user_line + def bp_commands(self, frame: FrameType) -> bool: ... - def interaction(self, frame: FrameType | None, traceback: TracebackType | None) -> None: ... + + if sys.version_info >= (3, 13): + def interaction(self, frame: FrameType | None, tb_or_exc: TracebackType | BaseException | None) -> None: ... + else: + def interaction(self, frame: FrameType | None, traceback: TracebackType | None) -> None: ... + def displayhook(self, obj: object) -> None: ... def handle_command_def(self, line: str) -> bool: ... def defaultFile(self) -> str: ... @@ -72,6 +83,9 @@ class Pdb(Bdb, Cmd): if sys.version_info < (3, 11): def _runscript(self, filename: str) -> None: ... + if sys.version_info >= (3, 13): + def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ... + def do_commands(self, arg: str) -> bool | None: ... def do_break(self, arg: str, temporary: bool = ...) -> bool | None: ... def do_tbreak(self, arg: str) -> bool | None: ... @@ -81,6 +95,9 @@ class Pdb(Bdb, Cmd): def do_ignore(self, arg: str) -> bool | None: ... def do_clear(self, arg: str) -> bool | None: ... def do_where(self, arg: str) -> bool | None: ... + if sys.version_info >= (3, 13): + def do_exceptions(self, arg: str) -> bool | None: ... + def do_up(self, arg: str) -> bool | None: ... def do_down(self, arg: str) -> bool | None: ... def do_until(self, arg: str) -> bool | None: ... @@ -125,8 +142,14 @@ class Pdb(Bdb, Cmd): def help_exec(self) -> None: ... def help_pdb(self) -> None: ... def sigint_handler(self, signum: signal.Signals, frame: FrameType) -> None: ... - def message(self, msg: str) -> None: ... + if sys.version_info >= (3, 13): + def message(self, msg: str, end: str = "\n") -> None: ... + else: + def message(self, msg: str) -> None: ... + def error(self, msg: str) -> None: ... + if sys.version_info >= (3, 13): + def completenames(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: ... # type: ignore[override] if sys.version_info >= (3, 12): def set_convenience_variable(self, frame: FrameType, name: str, value: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index 98ec80b0f14e2..9bea92ef1c9eb 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -157,10 +157,10 @@ class Pickler: def __init__( self, file: SupportsWrite[bytes], - protocol: int | None = ..., + protocol: int | None = None, *, - fix_imports: bool = ..., - buffer_callback: _BufferCallback = ..., + fix_imports: bool = True, + buffer_callback: _BufferCallback = None, ) -> None: ... def reducer_override(self, obj: Any) -> Any: ... def dump(self, obj: Any, /) -> None: ... @@ -174,10 +174,10 @@ class Unpickler: self, file: _ReadableFileobj, *, - fix_imports: bool = ..., - encoding: str = ..., - errors: str = ..., - buffers: Iterable[Any] | None = ..., + fix_imports: bool = True, + encoding: str = "ASCII", + errors: str = "strict", + buffers: Iterable[Any] | None = (), ) -> None: ... def load(self) -> Any: ... def find_class(self, module_name: str, global_name: str, /) -> Any: ... diff --git a/mypy/typeshed/stdlib/platform.pyi b/mypy/typeshed/stdlib/platform.pyi index c47ecdc51df49..73393eada02c2 100644 --- a/mypy/typeshed/stdlib/platform.pyi +++ b/mypy/typeshed/stdlib/platform.pyi @@ -1,5 +1,6 @@ import sys -from typing import NamedTuple +from typing import NamedTuple, type_check_only +from typing_extensions import Self def libc_ver(executable: str | None = None, lib: str = "", version: str = "", chunksize: int = 16384) -> tuple[str, str]: ... def win32_ver(release: str = "", version: str = "", csd: str = "", ptype: str = "") -> tuple[str, str, str, str]: ... @@ -14,13 +15,40 @@ def java_ver( def system_alias(system: str, release: str, version: str) -> tuple[str, str, str]: ... def architecture(executable: str = sys.executable, bits: str = "", linkage: str = "") -> tuple[str, str]: ... -class uname_result(NamedTuple): - system: str - node: str - release: str - version: str - machine: str - processor: str +if sys.version_info >= (3, 9): + # This class is not exposed. It calls itself platform.uname_result_base. + # At runtime it only has 5 fields. + @type_check_only + class _uname_result_base(NamedTuple): + system: str + node: str + release: str + version: str + machine: str + # This base class doesn't have this field at runtime, but claiming it + # does is the least bad way to handle the situation. Nobody really + # sees this class anyway. See #13068 + processor: str + + # uname_result emulates a 6-field named tuple, but the processor field + # is lazily evaluated rather than being passed in to the constructor. + class uname_result(_uname_result_base): + if sys.version_info >= (3, 10): + __match_args__ = ("system", "node", "release", "version", "machine") # pyright: ignore[reportAssignmentType] + + def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ... + @property + def processor(self) -> str: ... + +else: + # On 3.8, uname_result is actually just a regular NamedTuple. + class uname_result(NamedTuple): + system: str + node: str + release: str + version: str + machine: str + processor: str def uname() -> uname_result: ... def system() -> str: ... diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index 12f1d16a0d6fb..a1e41be86a7f8 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -3,7 +3,7 @@ import ssl import sys from builtins import list as _list # conflicts with a method named "list" from re import Pattern -from typing import Any, BinaryIO, Literal, NoReturn, overload +from typing import Any, BinaryIO, Final, NoReturn, overload from typing_extensions import TypeAlias __all__ = ["POP3", "error_proto", "POP3_SSL"] @@ -12,11 +12,11 @@ _LongResp: TypeAlias = tuple[bytes, list[bytes], int] class error_proto(Exception): ... -POP3_PORT: Literal[110] -POP3_SSL_PORT: Literal[995] -CR: Literal[b"\r"] -LF: Literal[b"\n"] -CRLF: Literal[b"\r\n"] +POP3_PORT: Final = 110 +POP3_SSL_PORT: Final = 995 +CR: Final = b"\r" +LF: Final = b"\n" +CRLF: Final = b"\r\n" HAVE_SSL: bool class POP3: @@ -67,5 +67,6 @@ class POP3_SSL(POP3): timeout: float = ..., context: ssl.SSLContext | None = None, ) -> None: ... - # "context" is actually the last argument, but that breaks LSP and it doesn't really matter because all the arguments are ignored + # "context" is actually the last argument, + # but that breaks LSP and it doesn't really matter because all the arguments are ignored def stls(self, context: Any = None, keyfile: Any = None, certfile: Any = None) -> NoReturn: ... diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index b31b8f3d35245..7a4d6cb4bdbef 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -29,22 +29,20 @@ if sys.platform != "win32": F_TLOCK as F_TLOCK, F_ULOCK as F_ULOCK, NGROUPS_MAX as NGROUPS_MAX, + O_ACCMODE as O_ACCMODE, O_APPEND as O_APPEND, O_ASYNC as O_ASYNC, + O_CLOEXEC as O_CLOEXEC, O_CREAT as O_CREAT, - O_DIRECT as O_DIRECT, O_DIRECTORY as O_DIRECTORY, O_DSYNC as O_DSYNC, O_EXCL as O_EXCL, - O_LARGEFILE as O_LARGEFILE, O_NDELAY as O_NDELAY, - O_NOATIME as O_NOATIME, O_NOCTTY as O_NOCTTY, O_NOFOLLOW as O_NOFOLLOW, O_NONBLOCK as O_NONBLOCK, O_RDONLY as O_RDONLY, O_RDWR as O_RDWR, - O_RSYNC as O_RSYNC, O_SYNC as O_SYNC, O_TRUNC as O_TRUNC, O_WRONLY as O_WRONLY, @@ -64,13 +62,9 @@ if sys.platform != "win32": RTLD_NODELETE as RTLD_NODELETE, RTLD_NOLOAD as RTLD_NOLOAD, RTLD_NOW as RTLD_NOW, - SCHED_BATCH as SCHED_BATCH, SCHED_FIFO as SCHED_FIFO, - SCHED_IDLE as SCHED_IDLE, SCHED_OTHER as SCHED_OTHER, - SCHED_RESET_ON_FORK as SCHED_RESET_ON_FORK, SCHED_RR as SCHED_RR, - SCHED_SPORADIC as SCHED_SPORADIC, SEEK_DATA as SEEK_DATA, SEEK_HOLE as SEEK_HOLE, ST_NOSUID as ST_NOSUID, @@ -233,14 +227,37 @@ if sys.platform != "win32": if sys.version_info >= (3, 9): from os import CLD_KILLED as CLD_KILLED, CLD_STOPPED as CLD_STOPPED, waitstatus_to_exitcode as waitstatus_to_exitcode + if sys.version_info >= (3, 10): + from os import O_FSYNC as O_FSYNC + if sys.version_info >= (3, 11): from os import login_tty as login_tty + if sys.version_info >= (3, 13): + from os import grantpt as grantpt, posix_openpt as posix_openpt, ptsname as ptsname, unlockpt as unlockpt + + if sys.version_info >= (3, 13) and sys.platform == "linux": + from os import ( + POSIX_SPAWN_CLOSEFROM as POSIX_SPAWN_CLOSEFROM, + TFD_CLOEXEC as TFD_CLOEXEC, + TFD_NONBLOCK as TFD_NONBLOCK, + TFD_TIMER_ABSTIME as TFD_TIMER_ABSTIME, + TFD_TIMER_CANCEL_ON_SET as TFD_TIMER_CANCEL_ON_SET, + timerfd_create as timerfd_create, + timerfd_gettime as timerfd_gettime, + timerfd_gettime_ns as timerfd_gettime_ns, + timerfd_settime as timerfd_settime, + timerfd_settime_ns as timerfd_settime_ns, + ) + if sys.platform != "linux": - from os import chflags as chflags, lchflags as lchflags, lchmod as lchmod + from os import O_EXLOCK as O_EXLOCK, O_SHLOCK as O_SHLOCK, chflags as chflags, lchflags as lchflags, lchmod as lchmod if sys.platform != "linux" and sys.platform != "darwin": - from os import EX_NOTFOUND as EX_NOTFOUND + from os import EX_NOTFOUND as EX_NOTFOUND, SCHED_SPORADIC as SCHED_SPORADIC + + if sys.platform != "linux" and sys.version_info >= (3, 13): + from os import O_EXEC as O_EXEC, O_SEARCH as O_SEARCH if sys.platform != "darwin": from os import ( @@ -254,6 +271,15 @@ if sys.platform != "win32": RWF_HIPRI as RWF_HIPRI, RWF_NOWAIT as RWF_NOWAIT, RWF_SYNC as RWF_SYNC, + ST_APPEND as ST_APPEND, + ST_MANDLOCK as ST_MANDLOCK, + ST_NOATIME as ST_NOATIME, + ST_NODEV as ST_NODEV, + ST_NODIRATIME as ST_NODIRATIME, + ST_NOEXEC as ST_NOEXEC, + ST_RELATIME as ST_RELATIME, + ST_SYNCHRONOUS as ST_SYNCHRONOUS, + ST_WRITE as ST_WRITE, fdatasync as fdatasync, getresgid as getresgid, getresuid as getresuid, @@ -269,13 +295,14 @@ if sys.platform != "win32": sched_setscheduler as sched_setscheduler, setresgid as setresgid, setresuid as setresuid, - waitid as waitid, - waitid_result as waitid_result, ) if sys.version_info >= (3, 10): from os import RWF_APPEND as RWF_APPEND + if sys.platform != "darwin" or sys.version_info >= (3, 13): + from os import waitid as waitid, waitid_result as waitid_result + if sys.platform == "linux": from os import ( GRND_NONBLOCK as GRND_NONBLOCK, @@ -297,7 +324,16 @@ if sys.platform != "win32": MFD_HUGE_MASK as MFD_HUGE_MASK, MFD_HUGE_SHIFT as MFD_HUGE_SHIFT, MFD_HUGETLB as MFD_HUGETLB, + O_DIRECT as O_DIRECT, + O_LARGEFILE as O_LARGEFILE, + O_NOATIME as O_NOATIME, + O_PATH as O_PATH, + O_RSYNC as O_RSYNC, + O_TMPFILE as O_TMPFILE, RTLD_DEEPBIND as RTLD_DEEPBIND, + SCHED_BATCH as SCHED_BATCH, + SCHED_IDLE as SCHED_IDLE, + SCHED_RESET_ON_FORK as SCHED_RESET_ON_FORK, XATTR_CREATE as XATTR_CREATE, XATTR_REPLACE as XATTR_REPLACE, XATTR_SIZE_MAX as XATTR_SIZE_MAX, @@ -355,6 +391,8 @@ if sys.platform != "win32": PRIO_DARWIN_PROCESS as PRIO_DARWIN_PROCESS, PRIO_DARWIN_THREAD as PRIO_DARWIN_THREAD, ) + if sys.platform == "darwin" and sys.version_info >= (3, 10): + from os import O_EVTONLY as O_EVTONLY, O_NOFOLLOW_ANY as O_NOFOLLOW_ANY, O_SYMLINK as O_SYMLINK # Not same as os.environ or os.environb # Because of this variable, we can't do "from posix import *" in os/__init__.pyi diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index e5f5fa0d813c1..3313667f1781b 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -77,7 +77,7 @@ pathsep: LiteralString defpath: LiteralString devnull: LiteralString -# Overloads are necessary to work around python/mypy#3644. +# Overloads are necessary to work around python/mypy#17952 & python/mypy#11880 @overload def abspath(path: PathLike[AnyStr]) -> AnyStr: ... @overload diff --git a/mypy/typeshed/stdlib/profile.pyi b/mypy/typeshed/stdlib/profile.pyi index 73eba36344fe6..696193d9dc169 100644 --- a/mypy/typeshed/stdlib/profile.pyi +++ b/mypy/typeshed/stdlib/profile.pyi @@ -1,5 +1,5 @@ from _typeshed import StrOrBytesPath -from collections.abc import Callable +from collections.abc import Callable, Mapping from typing import Any, TypeVar from typing_extensions import ParamSpec, Self, TypeAlias @@ -7,7 +7,7 @@ __all__ = ["run", "runctx", "Profile"] def run(statement: str, filename: str | None = None, sort: str | int = -1) -> None: ... def runctx( - statement: str, globals: dict[str, Any], locals: dict[str, Any], filename: str | None = None, sort: str | int = -1 + statement: str, globals: dict[str, Any], locals: Mapping[str, Any], filename: str | None = None, sort: str | int = -1 ) -> None: ... _T = TypeVar("_T") @@ -26,6 +26,6 @@ class Profile: def create_stats(self) -> None: ... def snapshot_stats(self) -> None: ... def run(self, cmd: str) -> Self: ... - def runctx(self, cmd: str, globals: dict[str, Any], locals: dict[str, Any]) -> Self: ... + def runctx(self, cmd: str, globals: dict[str, Any], locals: Mapping[str, Any]) -> Self: ... def runcall(self, func: Callable[_P, _T], /, *args: _P.args, **kw: _P.kwargs) -> _T: ... def calibrate(self, m: int, verbose: int = 0) -> float: ... diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index 83256b433035a..d41fa202cf779 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -1,11 +1,16 @@ import sys -from _typeshed import StrEnum, StrOrBytesPath +from _typeshed import StrOrBytesPath from collections.abc import Iterable from cProfile import Profile as _cProfile from profile import Profile from typing import IO, Any, Literal, overload from typing_extensions import Self, TypeAlias +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from enum import Enum + if sys.version_info >= (3, 9): __all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"] else: @@ -13,16 +18,29 @@ else: _Selector: TypeAlias = str | float | int -class SortKey(StrEnum): - CALLS = "calls" - CUMULATIVE = "cumulative" - FILENAME = "filename" - LINE = "line" - NAME = "name" - NFL = "nfl" - PCALLS = "pcalls" - STDNAME = "stdname" - TIME = "time" +if sys.version_info >= (3, 11): + class SortKey(StrEnum): + CALLS = "calls" + CUMULATIVE = "cumulative" + FILENAME = "filename" + LINE = "line" + NAME = "name" + NFL = "nfl" + PCALLS = "pcalls" + STDNAME = "stdname" + TIME = "time" + +else: + class SortKey(str, Enum): + CALLS = "calls" + CUMULATIVE = "cumulative" + FILENAME = "filename" + LINE = "line" + NAME = "name" + NFL = "nfl" + PCALLS = "pcalls" + STDNAME = "stdname" + TIME = "time" if sys.version_info >= (3, 9): from dataclasses import dataclass diff --git a/mypy/typeshed/stdlib/pty.pyi b/mypy/typeshed/stdlib/pty.pyi index 022b08046c542..941915179c4a5 100644 --- a/mypy/typeshed/stdlib/pty.pyi +++ b/mypy/typeshed/stdlib/pty.pyi @@ -1,19 +1,24 @@ import sys from collections.abc import Callable, Iterable -from typing import Literal -from typing_extensions import TypeAlias +from typing import Final +from typing_extensions import TypeAlias, deprecated if sys.platform != "win32": __all__ = ["openpty", "fork", "spawn"] _Reader: TypeAlias = Callable[[int], bytes] - STDIN_FILENO: Literal[0] - STDOUT_FILENO: Literal[1] - STDERR_FILENO: Literal[2] + STDIN_FILENO: Final = 0 + STDOUT_FILENO: Final = 1 + STDERR_FILENO: Final = 2 - CHILD: Literal[0] + CHILD: Final = 0 def openpty() -> tuple[int, int]: ... - def master_open() -> tuple[int, str]: ... # deprecated, use openpty() - def slave_open(tty_name: str) -> int: ... # deprecated, use openpty() + + if sys.version_info < (3, 14): + @deprecated("Deprecated in 3.12, to be removed in 3.14; use openpty() instead") + def master_open() -> tuple[int, str]: ... + @deprecated("Deprecated in 3.12, to be removed in 3.14; use openpty() instead") + def slave_open(tty_name: str) -> int: ... + def fork() -> tuple[int, int]: ... def spawn(argv: str | Iterable[str], master_read: _Reader = ..., stdin_read: _Reader = ...) -> int: ... diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index 1a90eb30efca4..144f782acad57 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -5,7 +5,7 @@ from builtins import list as _list # "list" conflicts with method name from collections.abc import Callable, Container, Mapping, MutableMapping from reprlib import Repr from types import MethodType, ModuleType, TracebackType -from typing import IO, Any, AnyStr, Final, NoReturn, TypeVar +from typing import IO, Any, AnyStr, Final, NoReturn, Protocol, TypeVar from typing_extensions import TypeGuard __all__ = ["help"] @@ -17,6 +17,9 @@ __date__: Final[str] __version__: Final[str] __credits__: Final[str] +class _Pager(Protocol): + def __call__(self, text: str, title: str = "") -> None: ... + def pathdirs() -> list[str]: ... def getdoc(object: object) -> str: ... def splitdoc(doc: AnyStr) -> tuple[AnyStr, AnyStr]: ... @@ -229,16 +232,36 @@ class TextDoc(Doc): doc: Any | None = None, ) -> str: ... -def pager(text: str) -> None: ... -def getpager() -> Callable[[str], None]: ... +if sys.version_info >= (3, 13): + def pager(text: str, title: str = "") -> None: ... + +else: + def pager(text: str) -> None: ... + def plain(text: str) -> str: ... -def pipepager(text: str, cmd: str) -> None: ... -def tempfilepager(text: str, cmd: str) -> None: ... -def ttypager(text: str) -> None: ... -def plainpager(text: str) -> None: ... def describe(thing: Any) -> str: ... def locate(path: str, forceload: bool = ...) -> object: ... +if sys.version_info >= (3, 13): + def get_pager() -> _Pager: ... + def pipe_pager(text: str, cmd: str, title: str = "") -> None: ... + def tempfile_pager(text: str, cmd: str, title: str = "") -> None: ... + def tty_pager(text: str, title: str = "") -> None: ... + def plain_pager(text: str, title: str = "") -> None: ... + + # For backwards compatibility. + getpager = get_pager + pipepager = pipe_pager + tempfilepager = tempfile_pager + ttypager = tty_pager + plainpager = plain_pager +else: + def getpager() -> Callable[[str], None]: ... + def pipepager(text: str, cmd: str) -> None: ... + def tempfilepager(text: str, cmd: str) -> None: ... + def ttypager(text: str) -> None: ... + def plainpager(text: str) -> None: ... + text: TextDoc html: HTMLDoc diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 88bf9464d130c..21e676052098d 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -1,24 +1,19 @@ from _typeshed import ReadableBuffer, SupportsRead from collections.abc import Callable from pyexpat import errors as errors, model as model -from typing import Any, final -from typing_extensions import TypeAlias +from typing import Any, Final, final +from typing_extensions import CapsuleType, TypeAlias +from xml.parsers.expat import ExpatError as ExpatError -EXPAT_VERSION: str # undocumented +EXPAT_VERSION: Final[str] # undocumented version_info: tuple[int, int, int] # undocumented native_encoding: str # undocumented features: list[tuple[str, int]] # undocumented -class ExpatError(Exception): - code: int - lineno: int - offset: int - error = ExpatError - -XML_PARAM_ENTITY_PARSING_NEVER: int -XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: int -XML_PARAM_ENTITY_PARSING_ALWAYS: int +XML_PARAM_ENTITY_PARSING_NEVER: Final = 0 +XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: Final = 1 +XML_PARAM_ENTITY_PARSING_ALWAYS: Final = 2 _Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]] @@ -83,3 +78,5 @@ def ErrorString(code: int, /) -> str: ... def ParserCreate( encoding: str | None = None, namespace_separator: str | None = None, intern: dict[str, Any] | None = None ) -> XMLParserType: ... + +expat_CAPI: CapsuleType diff --git a/mypy/typeshed/stdlib/pyexpat/errors.pyi b/mypy/typeshed/stdlib/pyexpat/errors.pyi index 2e512eb129891..cae4da089161f 100644 --- a/mypy/typeshed/stdlib/pyexpat/errors.pyi +++ b/mypy/typeshed/stdlib/pyexpat/errors.pyi @@ -1,49 +1,51 @@ import sys +from typing import Final +from typing_extensions import LiteralString codes: dict[str, int] messages: dict[int, str] -XML_ERROR_ABORTED: str -XML_ERROR_ASYNC_ENTITY: str -XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: str -XML_ERROR_BAD_CHAR_REF: str -XML_ERROR_BINARY_ENTITY_REF: str -XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING: str -XML_ERROR_DUPLICATE_ATTRIBUTE: str -XML_ERROR_ENTITY_DECLARED_IN_PE: str -XML_ERROR_EXTERNAL_ENTITY_HANDLING: str -XML_ERROR_FEATURE_REQUIRES_XML_DTD: str -XML_ERROR_FINISHED: str -XML_ERROR_INCOMPLETE_PE: str -XML_ERROR_INCORRECT_ENCODING: str -XML_ERROR_INVALID_TOKEN: str -XML_ERROR_JUNK_AFTER_DOC_ELEMENT: str -XML_ERROR_MISPLACED_XML_PI: str -XML_ERROR_NOT_STANDALONE: str -XML_ERROR_NOT_SUSPENDED: str -XML_ERROR_NO_ELEMENTS: str -XML_ERROR_NO_MEMORY: str -XML_ERROR_PARAM_ENTITY_REF: str -XML_ERROR_PARTIAL_CHAR: str -XML_ERROR_PUBLICID: str -XML_ERROR_RECURSIVE_ENTITY_REF: str -XML_ERROR_SUSPENDED: str -XML_ERROR_SUSPEND_PE: str -XML_ERROR_SYNTAX: str -XML_ERROR_TAG_MISMATCH: str -XML_ERROR_TEXT_DECL: str -XML_ERROR_UNBOUND_PREFIX: str -XML_ERROR_UNCLOSED_CDATA_SECTION: str -XML_ERROR_UNCLOSED_TOKEN: str -XML_ERROR_UNDECLARING_PREFIX: str -XML_ERROR_UNDEFINED_ENTITY: str -XML_ERROR_UNEXPECTED_STATE: str -XML_ERROR_UNKNOWN_ENCODING: str -XML_ERROR_XML_DECL: str +XML_ERROR_ABORTED: Final[LiteralString] +XML_ERROR_ASYNC_ENTITY: Final[LiteralString] +XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: Final[LiteralString] +XML_ERROR_BAD_CHAR_REF: Final[LiteralString] +XML_ERROR_BINARY_ENTITY_REF: Final[LiteralString] +XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING: Final[LiteralString] +XML_ERROR_DUPLICATE_ATTRIBUTE: Final[LiteralString] +XML_ERROR_ENTITY_DECLARED_IN_PE: Final[LiteralString] +XML_ERROR_EXTERNAL_ENTITY_HANDLING: Final[LiteralString] +XML_ERROR_FEATURE_REQUIRES_XML_DTD: Final[LiteralString] +XML_ERROR_FINISHED: Final[LiteralString] +XML_ERROR_INCOMPLETE_PE: Final[LiteralString] +XML_ERROR_INCORRECT_ENCODING: Final[LiteralString] +XML_ERROR_INVALID_TOKEN: Final[LiteralString] +XML_ERROR_JUNK_AFTER_DOC_ELEMENT: Final[LiteralString] +XML_ERROR_MISPLACED_XML_PI: Final[LiteralString] +XML_ERROR_NOT_STANDALONE: Final[LiteralString] +XML_ERROR_NOT_SUSPENDED: Final[LiteralString] +XML_ERROR_NO_ELEMENTS: Final[LiteralString] +XML_ERROR_NO_MEMORY: Final[LiteralString] +XML_ERROR_PARAM_ENTITY_REF: Final[LiteralString] +XML_ERROR_PARTIAL_CHAR: Final[LiteralString] +XML_ERROR_PUBLICID: Final[LiteralString] +XML_ERROR_RECURSIVE_ENTITY_REF: Final[LiteralString] +XML_ERROR_SUSPENDED: Final[LiteralString] +XML_ERROR_SUSPEND_PE: Final[LiteralString] +XML_ERROR_SYNTAX: Final[LiteralString] +XML_ERROR_TAG_MISMATCH: Final[LiteralString] +XML_ERROR_TEXT_DECL: Final[LiteralString] +XML_ERROR_UNBOUND_PREFIX: Final[LiteralString] +XML_ERROR_UNCLOSED_CDATA_SECTION: Final[LiteralString] +XML_ERROR_UNCLOSED_TOKEN: Final[LiteralString] +XML_ERROR_UNDECLARING_PREFIX: Final[LiteralString] +XML_ERROR_UNDEFINED_ENTITY: Final[LiteralString] +XML_ERROR_UNEXPECTED_STATE: Final[LiteralString] +XML_ERROR_UNKNOWN_ENCODING: Final[LiteralString] +XML_ERROR_XML_DECL: Final[LiteralString] if sys.version_info >= (3, 11): - XML_ERROR_RESERVED_PREFIX_XML: str - XML_ERROR_RESERVED_PREFIX_XMLNS: str - XML_ERROR_RESERVED_NAMESPACE_URI: str - XML_ERROR_INVALID_ARGUMENT: str - XML_ERROR_NO_BUFFER: str - XML_ERROR_AMPLIFICATION_LIMIT_BREACH: str + XML_ERROR_RESERVED_PREFIX_XML: Final[LiteralString] + XML_ERROR_RESERVED_PREFIX_XMLNS: Final[LiteralString] + XML_ERROR_RESERVED_NAMESPACE_URI: Final[LiteralString] + XML_ERROR_INVALID_ARGUMENT: Final[LiteralString] + XML_ERROR_NO_BUFFER: Final[LiteralString] + XML_ERROR_AMPLIFICATION_LIMIT_BREACH: Final[LiteralString] diff --git a/mypy/typeshed/stdlib/pyexpat/model.pyi b/mypy/typeshed/stdlib/pyexpat/model.pyi index f357cf6511a26..bac8f3692ce58 100644 --- a/mypy/typeshed/stdlib/pyexpat/model.pyi +++ b/mypy/typeshed/stdlib/pyexpat/model.pyi @@ -1,11 +1,13 @@ -XML_CTYPE_ANY: int -XML_CTYPE_CHOICE: int -XML_CTYPE_EMPTY: int -XML_CTYPE_MIXED: int -XML_CTYPE_NAME: int -XML_CTYPE_SEQ: int +from typing import Final -XML_CQUANT_NONE: int -XML_CQUANT_OPT: int -XML_CQUANT_PLUS: int -XML_CQUANT_REP: int +XML_CTYPE_ANY: Final = 2 +XML_CTYPE_EMPTY: Final = 1 +XML_CTYPE_MIXED: Final = 3 +XML_CTYPE_NAME: Final = 4 +XML_CTYPE_CHOICE: Final = 5 +XML_CTYPE_SEQ: Final = 6 + +XML_CQUANT_NONE: Final = 0 +XML_CQUANT_OPT: Final = 1 +XML_CQUANT_REP: Final = 2 +XML_CQUANT_PLUS: Final = 3 diff --git a/mypy/typeshed/stdlib/queue.pyi b/mypy/typeshed/stdlib/queue.pyi index 16643c99d08df..4fb49cb6102b3 100644 --- a/mypy/typeshed/stdlib/queue.pyi +++ b/mypy/typeshed/stdlib/queue.pyi @@ -1,4 +1,5 @@ import sys +from _queue import Empty as Empty, SimpleQueue as SimpleQueue from threading import Condition, Lock from typing import Any, Generic, TypeVar @@ -11,7 +12,6 @@ if sys.version_info >= (3, 13): _T = TypeVar("_T") -class Empty(Exception): ... class Full(Exception): ... if sys.version_info >= (3, 13): @@ -55,14 +55,3 @@ class PriorityQueue(Queue[_T]): class LifoQueue(Queue[_T]): queue: list[_T] - -class SimpleQueue(Generic[_T]): - def __init__(self) -> None: ... - def empty(self) -> bool: ... - def get(self, block: bool = True, timeout: float | None = None) -> _T: ... - def get_nowait(self) -> _T: ... - def put(self, item: _T, block: bool = True, timeout: float | None = None) -> None: ... - def put_nowait(self, item: _T) -> None: ... - def qsize(self) -> int: ... - if sys.version_info >= (3, 9): - def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index b06f494c0b7d6..b8fe2e9e1a46d 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -2,9 +2,8 @@ import enum import sre_compile import sre_constants import sys -from _typeshed import ReadableBuffer +from _typeshed import MaybeNone, ReadableBuffer from collections.abc import Callable, Iterator, Mapping -from sre_constants import error as error from typing import Any, AnyStr, Generic, Literal, TypeVar, final, overload from typing_extensions import TypeAlias @@ -54,6 +53,16 @@ if sys.version_info >= (3, 13): _T = TypeVar("_T") +# The implementation defines this in re._constants (version_info >= 3, 11) or +# sre_constants. Typeshed has it here because its __module__ attribute is set to "re". +class error(Exception): + msg: str + pattern: str | bytes | None + pos: int | None + lineno: int + colno: int + def __init__(self, msg: str, pattern: str | bytes | None = None, pos: int | None = None) -> None: ... + @final class Match(Generic[AnyStr]): @property @@ -74,26 +83,26 @@ class Match(Generic[AnyStr]): @overload def expand(self: Match[str], template: str) -> str: ... @overload - def expand(self: Match[bytes], template: ReadableBuffer) -> bytes: ... # type: ignore[overload-overlap] + def expand(self: Match[bytes], template: ReadableBuffer) -> bytes: ... @overload def expand(self, template: AnyStr) -> AnyStr: ... # group() returns "AnyStr" or "AnyStr | None", depending on the pattern. @overload def group(self, group: Literal[0] = 0, /) -> AnyStr: ... @overload - def group(self, group: str | int, /) -> AnyStr | Any: ... + def group(self, group: str | int, /) -> AnyStr | MaybeNone: ... @overload - def group(self, group1: str | int, group2: str | int, /, *groups: str | int) -> tuple[AnyStr | Any, ...]: ... + def group(self, group1: str | int, group2: str | int, /, *groups: str | int) -> tuple[AnyStr | MaybeNone, ...]: ... # Each item of groups()'s return tuple is either "AnyStr" or # "AnyStr | None", depending on the pattern. @overload - def groups(self) -> tuple[AnyStr | Any, ...]: ... + def groups(self) -> tuple[AnyStr | MaybeNone, ...]: ... @overload def groups(self, default: _T) -> tuple[AnyStr | _T, ...]: ... # Each value in groupdict()'s return dict is either "AnyStr" or # "AnyStr | None", depending on the pattern. @overload - def groupdict(self) -> dict[str, AnyStr | Any]: ... + def groupdict(self) -> dict[str, AnyStr | MaybeNone]: ... @overload def groupdict(self, default: _T) -> dict[str, AnyStr | _T]: ... def start(self, group: int | str = 0, /) -> int: ... @@ -105,7 +114,7 @@ class Match(Generic[AnyStr]): @overload def __getitem__(self, key: Literal[0], /) -> AnyStr: ... @overload - def __getitem__(self, key: int | str, /) -> AnyStr | Any: ... + def __getitem__(self, key: int | str, /) -> AnyStr | MaybeNone: ... def __copy__(self) -> Match[AnyStr]: ... def __deepcopy__(self, memo: Any, /) -> Match[AnyStr]: ... if sys.version_info >= (3, 9): @@ -124,27 +133,29 @@ class Pattern(Generic[AnyStr]): @overload def search(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def search(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] + def search(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... @overload def search(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload def match(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def match(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] + def match(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... @overload def match(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload def fullmatch(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Match[str] | None: ... @overload - def fullmatch(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Match[bytes] | None: ... # type: ignore[overload-overlap] + def fullmatch( + self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize + ) -> Match[bytes] | None: ... @overload def fullmatch(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Match[AnyStr] | None: ... @overload - def split(self: Pattern[str], string: str, maxsplit: int = 0) -> list[str | Any]: ... + def split(self: Pattern[str], string: str, maxsplit: int = 0) -> list[str | MaybeNone]: ... @overload - def split(self: Pattern[bytes], string: ReadableBuffer, maxsplit: int = 0) -> list[bytes | Any]: ... + def split(self: Pattern[bytes], string: ReadableBuffer, maxsplit: int = 0) -> list[bytes | MaybeNone]: ... @overload - def split(self, string: AnyStr, maxsplit: int = 0) -> list[AnyStr | Any]: ... + def split(self, string: AnyStr, maxsplit: int = 0) -> list[AnyStr | MaybeNone]: ... # return type depends on the number of groups in the pattern @overload def findall(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> list[Any]: ... @@ -155,13 +166,15 @@ class Pattern(Generic[AnyStr]): @overload def finditer(self: Pattern[str], string: str, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[str]]: ... @overload - def finditer(self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[bytes]]: ... # type: ignore[overload-overlap] + def finditer( + self: Pattern[bytes], string: ReadableBuffer, pos: int = 0, endpos: int = sys.maxsize + ) -> Iterator[Match[bytes]]: ... @overload def finditer(self, string: AnyStr, pos: int = 0, endpos: int = sys.maxsize) -> Iterator[Match[AnyStr]]: ... @overload def sub(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = 0) -> str: ... @overload - def sub( # type: ignore[overload-overlap] + def sub( self: Pattern[bytes], repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], string: ReadableBuffer, @@ -172,7 +185,7 @@ class Pattern(Generic[AnyStr]): @overload def subn(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = 0) -> tuple[str, int]: ... @overload - def subn( # type: ignore[overload-overlap] + def subn( self: Pattern[bytes], repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], string: ReadableBuffer, @@ -257,11 +270,11 @@ def fullmatch(pattern: str | Pattern[str], string: str, flags: _FlagsType = 0) - @overload def fullmatch(pattern: bytes | Pattern[bytes], string: ReadableBuffer, flags: _FlagsType = 0) -> Match[bytes] | None: ... @overload -def split(pattern: str | Pattern[str], string: str, maxsplit: int = 0, flags: _FlagsType = 0) -> list[str | Any]: ... +def split(pattern: str | Pattern[str], string: str, maxsplit: int = 0, flags: _FlagsType = 0) -> list[str | MaybeNone]: ... @overload def split( pattern: bytes | Pattern[bytes], string: ReadableBuffer, maxsplit: int = 0, flags: _FlagsType = 0 -) -> list[bytes | Any]: ... +) -> list[bytes | MaybeNone]: ... @overload def findall(pattern: str | Pattern[str], string: str, flags: _FlagsType = 0) -> list[Any]: ... @overload diff --git a/mypy/typeshed/stdlib/readline.pyi b/mypy/typeshed/stdlib/readline.pyi index 688ae48d9f924..7325c267b32c2 100644 --- a/mypy/typeshed/stdlib/readline.pyi +++ b/mypy/typeshed/stdlib/readline.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import StrOrBytesPath from collections.abc import Callable, Sequence +from typing import Literal from typing_extensions import TypeAlias if sys.platform != "win32": @@ -34,3 +35,6 @@ if sys.platform != "win32": def set_completer_delims(string: str, /) -> None: ... def get_completer_delims() -> str: ... def set_completion_display_matches_hook(function: _CompDisp | None = None, /) -> None: ... + + if sys.version_info >= (3, 13): + backend: Literal["readline", "editline"] diff --git a/mypy/typeshed/stdlib/sched.pyi b/mypy/typeshed/stdlib/sched.pyi index 75dd63d0414af..ace5014308471 100644 --- a/mypy/typeshed/stdlib/sched.pyi +++ b/mypy/typeshed/stdlib/sched.pyi @@ -1,6 +1,6 @@ import sys from collections.abc import Callable -from typing import Any, NamedTuple +from typing import Any, NamedTuple, type_check_only from typing_extensions import TypeAlias __all__ = ["scheduler"] @@ -17,13 +17,16 @@ if sys.version_info >= (3, 10): kwargs: dict[str, Any] else: - class Event(NamedTuple): + @type_check_only + class _EventBase(NamedTuple): time: float priority: Any action: _ActionCallback argument: tuple[Any, ...] kwargs: dict[str, Any] + class Event(_EventBase): ... + class scheduler: timefunc: Callable[[], float] delayfunc: Callable[[float], object] diff --git a/mypy/typeshed/stdlib/shlex.pyi b/mypy/typeshed/stdlib/shlex.pyi index daa8df439b269..1c27483782fb5 100644 --- a/mypy/typeshed/stdlib/shlex.pyi +++ b/mypy/typeshed/stdlib/shlex.pyi @@ -27,7 +27,7 @@ def join(split_command: Iterable[str]) -> str: ... def quote(s: str) -> str: ... # TODO: Make generic over infile once PEP 696 is implemented. -class shlex(Iterable[str]): +class shlex: commenters: str wordchars: str whitespace: str diff --git a/mypy/typeshed/stdlib/site.pyi b/mypy/typeshed/stdlib/site.pyi index a8c6bcb417f4b..6e39677aaea0e 100644 --- a/mypy/typeshed/stdlib/site.pyi +++ b/mypy/typeshed/stdlib/site.pyi @@ -1,3 +1,4 @@ +import sys from _typeshed import StrPath from collections.abc import Iterable @@ -13,7 +14,15 @@ def addsitedir(sitedir: str, known_paths: set[str] | None = None) -> None: ... def addsitepackages(known_paths: set[str] | None, prefixes: Iterable[str] | None = None) -> set[str] | None: ... # undocumented def addusersitepackages(known_paths: set[str] | None) -> set[str] | None: ... # undocumented def check_enableusersite() -> bool | None: ... # undocumented + +if sys.version_info >= (3, 13): + def gethistoryfile() -> str: ... # undocumented + def enablerlcompleter() -> None: ... # undocumented + +if sys.version_info >= (3, 13): + def register_readline() -> None: ... # undocumented + def execsitecustomize() -> None: ... # undocumented def execusercustomize() -> None: ... # undocumented def getsitepackages(prefixes: Iterable[str] | None = None) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index b626409d2dde9..e42bba757fc36 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -28,7 +28,6 @@ from _socket import ( IP_MULTICAST_LOOP as IP_MULTICAST_LOOP, IP_MULTICAST_TTL as IP_MULTICAST_TTL, IP_OPTIONS as IP_OPTIONS, - IP_RECVDSTADDR as IP_RECVDSTADDR, IP_TOS as IP_TOS, IP_TTL as IP_TTL, IPPORT_RESERVED as IPPORT_RESERVED, @@ -38,17 +37,13 @@ from _socket import ( IPPROTO_EGP as IPPROTO_EGP, IPPROTO_ESP as IPPROTO_ESP, IPPROTO_FRAGMENT as IPPROTO_FRAGMENT, - IPPROTO_GGP as IPPROTO_GGP, IPPROTO_HOPOPTS as IPPROTO_HOPOPTS, IPPROTO_ICMP as IPPROTO_ICMP, IPPROTO_ICMPV6 as IPPROTO_ICMPV6, IPPROTO_IDP as IPPROTO_IDP, IPPROTO_IGMP as IPPROTO_IGMP, IPPROTO_IP as IPPROTO_IP, - IPPROTO_IPV4 as IPPROTO_IPV4, IPPROTO_IPV6 as IPPROTO_IPV6, - IPPROTO_MAX as IPPROTO_MAX, - IPPROTO_ND as IPPROTO_ND, IPPROTO_NONE as IPPROTO_NONE, IPPROTO_PIM as IPPROTO_PIM, IPPROTO_PUP as IPPROTO_PUP, @@ -93,7 +88,6 @@ from _socket import ( SO_SNDLOWAT as SO_SNDLOWAT, SO_SNDTIMEO as SO_SNDTIMEO, SO_TYPE as SO_TYPE, - SO_USELOOPBACK as SO_USELOOPBACK, SOL_IP as SOL_IP, SOL_SOCKET as SOL_SOCKET, SOL_TCP as SOL_TCP, @@ -109,8 +103,6 @@ from _socket import ( _RetAddress as _RetAddress, close as close, dup as dup, - error as error, - gaierror as gaierror, getdefaulttimeout as getdefaulttimeout, gethostbyaddr as gethostbyaddr, gethostbyname as gethostbyname, @@ -121,7 +113,6 @@ from _socket import ( getservbyname as getservbyname, getservbyport as getservbyport, has_ipv6 as has_ipv6, - herror as herror, htonl as htonl, htons as htons, if_indextoname as if_indextoname, @@ -134,7 +125,6 @@ from _socket import ( ntohl as ntohl, ntohs as ntohs, setdefaulttimeout as setdefaulttimeout, - timeout as timeout, ) from _typeshed import ReadableBuffer, Unused, WriteableBuffer from collections.abc import Iterable @@ -143,8 +133,176 @@ from io import BufferedReader, BufferedRWPair, BufferedWriter, IOBase, RawIOBase from typing import Any, Literal, Protocol, SupportsIndex, overload from typing_extensions import Self +__all__ = [ + "fromfd", + "getfqdn", + "create_connection", + "create_server", + "has_dualstack_ipv6", + "AddressFamily", + "SocketKind", + "AF_APPLETALK", + "AF_DECnet", + "AF_INET", + "AF_INET6", + "AF_IPX", + "AF_SNA", + "AF_UNSPEC", + "AI_ADDRCONFIG", + "AI_ALL", + "AI_CANONNAME", + "AI_NUMERICHOST", + "AI_NUMERICSERV", + "AI_PASSIVE", + "AI_V4MAPPED", + "CAPI", + "EAI_AGAIN", + "EAI_BADFLAGS", + "EAI_FAIL", + "EAI_FAMILY", + "EAI_MEMORY", + "EAI_NODATA", + "EAI_NONAME", + "EAI_SERVICE", + "EAI_SOCKTYPE", + "INADDR_ALLHOSTS_GROUP", + "INADDR_ANY", + "INADDR_BROADCAST", + "INADDR_LOOPBACK", + "INADDR_MAX_LOCAL_GROUP", + "INADDR_NONE", + "INADDR_UNSPEC_GROUP", + "IPPORT_RESERVED", + "IPPORT_USERRESERVED", + "IPPROTO_AH", + "IPPROTO_DSTOPTS", + "IPPROTO_EGP", + "IPPROTO_ESP", + "IPPROTO_FRAGMENT", + "IPPROTO_HOPOPTS", + "IPPROTO_ICMP", + "IPPROTO_ICMPV6", + "IPPROTO_IDP", + "IPPROTO_IGMP", + "IPPROTO_IP", + "IPPROTO_IPV6", + "IPPROTO_NONE", + "IPPROTO_PIM", + "IPPROTO_PUP", + "IPPROTO_RAW", + "IPPROTO_ROUTING", + "IPPROTO_SCTP", + "IPPROTO_TCP", + "IPPROTO_UDP", + "IPV6_CHECKSUM", + "IPV6_JOIN_GROUP", + "IPV6_LEAVE_GROUP", + "IPV6_MULTICAST_HOPS", + "IPV6_MULTICAST_IF", + "IPV6_MULTICAST_LOOP", + "IPV6_RECVTCLASS", + "IPV6_TCLASS", + "IPV6_UNICAST_HOPS", + "IPV6_V6ONLY", + "IP_ADD_MEMBERSHIP", + "IP_DROP_MEMBERSHIP", + "IP_HDRINCL", + "IP_MULTICAST_IF", + "IP_MULTICAST_LOOP", + "IP_MULTICAST_TTL", + "IP_OPTIONS", + "IP_TOS", + "IP_TTL", + "MSG_CTRUNC", + "MSG_DONTROUTE", + "MSG_OOB", + "MSG_PEEK", + "MSG_TRUNC", + "MSG_WAITALL", + "NI_DGRAM", + "NI_MAXHOST", + "NI_MAXSERV", + "NI_NAMEREQD", + "NI_NOFQDN", + "NI_NUMERICHOST", + "NI_NUMERICSERV", + "SHUT_RD", + "SHUT_RDWR", + "SHUT_WR", + "SOCK_DGRAM", + "SOCK_RAW", + "SOCK_RDM", + "SOCK_SEQPACKET", + "SOCK_STREAM", + "SOL_IP", + "SOL_SOCKET", + "SOL_TCP", + "SOL_UDP", + "SOMAXCONN", + "SO_ACCEPTCONN", + "SO_BROADCAST", + "SO_DEBUG", + "SO_DONTROUTE", + "SO_ERROR", + "SO_KEEPALIVE", + "SO_LINGER", + "SO_OOBINLINE", + "SO_RCVBUF", + "SO_RCVLOWAT", + "SO_RCVTIMEO", + "SO_REUSEADDR", + "SO_SNDBUF", + "SO_SNDLOWAT", + "SO_SNDTIMEO", + "SO_TYPE", + "SocketType", + "TCP_FASTOPEN", + "TCP_KEEPCNT", + "TCP_KEEPINTVL", + "TCP_MAXSEG", + "TCP_NODELAY", + "close", + "dup", + "error", + "gaierror", + "getaddrinfo", + "getdefaulttimeout", + "gethostbyaddr", + "gethostbyname", + "gethostbyname_ex", + "gethostname", + "getnameinfo", + "getprotobyname", + "getservbyname", + "getservbyport", + "has_ipv6", + "herror", + "htonl", + "htons", + "if_indextoname", + "if_nameindex", + "if_nametoindex", + "inet_aton", + "inet_ntoa", + "inet_ntop", + "inet_pton", + "ntohl", + "ntohs", + "setdefaulttimeout", + "socket", + "socketpair", + "timeout", +] + if sys.platform == "win32": from _socket import ( + IPPROTO_CBT as IPPROTO_CBT, + IPPROTO_ICLFXBM as IPPROTO_ICLFXBM, + IPPROTO_IGP as IPPROTO_IGP, + IPPROTO_L2TP as IPPROTO_L2TP, + IPPROTO_PGM as IPPROTO_PGM, + IPPROTO_RDP as IPPROTO_RDP, + IPPROTO_ST as IPPROTO_ST, RCVALL_MAX as RCVALL_MAX, RCVALL_OFF as RCVALL_OFF, RCVALL_ON as RCVALL_ON, @@ -155,6 +313,28 @@ if sys.platform == "win32": SO_EXCLUSIVEADDRUSE as SO_EXCLUSIVEADDRUSE, ) + __all__ += [ + "IPPROTO_CBT", + "IPPROTO_ICLFXBM", + "IPPROTO_IGP", + "IPPROTO_L2TP", + "IPPROTO_PGM", + "IPPROTO_RDP", + "IPPROTO_ST", + "RCVALL_MAX", + "RCVALL_OFF", + "RCVALL_ON", + "RCVALL_SOCKETLEVELONLY", + "SIO_KEEPALIVE_VALS", + "SIO_LOOPBACK_FAST_PATH", + "SIO_RCVALL", + "SO_EXCLUSIVEADDRUSE", + "fromshare", + "errorTab", + "MSG_BCAST", + "MSG_MCAST", + ] + if sys.platform != "darwin" or sys.version_info >= (3, 9): from _socket import ( IPV6_DONTFRAG as IPV6_DONTFRAG, @@ -165,33 +345,26 @@ if sys.platform != "darwin" or sys.version_info >= (3, 9): IPV6_RTHDR as IPV6_RTHDR, ) + __all__ += ["IPV6_DONTFRAG", "IPV6_HOPLIMIT", "IPV6_HOPOPTS", "IPV6_PKTINFO", "IPV6_RECVRTHDR", "IPV6_RTHDR"] + if sys.platform == "darwin": from _socket import PF_SYSTEM as PF_SYSTEM, SYSPROTO_CONTROL as SYSPROTO_CONTROL + __all__ += ["PF_SYSTEM", "SYSPROTO_CONTROL", "AF_SYSTEM"] + if sys.platform != "darwin": - from _socket import ( - IPPROTO_CBT as IPPROTO_CBT, - IPPROTO_ICLFXBM as IPPROTO_ICLFXBM, - IPPROTO_IGP as IPPROTO_IGP, - IPPROTO_L2TP as IPPROTO_L2TP, - IPPROTO_PGM as IPPROTO_PGM, - IPPROTO_RDP as IPPROTO_RDP, - IPPROTO_ST as IPPROTO_ST, - TCP_KEEPIDLE as TCP_KEEPIDLE, - ) + from _socket import TCP_KEEPIDLE as TCP_KEEPIDLE + + __all__ += ["TCP_KEEPIDLE", "AF_IRDA", "MSG_ERRQUEUE"] if sys.version_info >= (3, 10): from _socket import IP_RECVTOS as IP_RECVTOS -elif sys.platform != "win32" and sys.platform != "darwin": - from _socket import IP_RECVTOS as IP_RECVTOS + + __all__ += ["IP_RECVTOS"] if sys.platform != "win32" and sys.platform != "darwin": from _socket import ( - IP_BIND_ADDRESS_NO_PORT as IP_BIND_ADDRESS_NO_PORT, IP_TRANSPARENT as IP_TRANSPARENT, - IPPROTO_BIP as IPPROTO_BIP, - IPPROTO_MOBILE as IPPROTO_MOBILE, - IPPROTO_VRRP as IPPROTO_VRRP, IPX_TYPE as IPX_TYPE, SCM_CREDENTIALS as SCM_CREDENTIALS, SO_BINDTODEVICE as SO_BINDTODEVICE, @@ -203,7 +376,6 @@ if sys.platform != "win32" and sys.platform != "darwin": SO_PEERSEC as SO_PEERSEC, SO_PRIORITY as SO_PRIORITY, SO_PROTOCOL as SO_PROTOCOL, - SO_SETFIB as SO_SETFIB, SOL_ATALK as SOL_ATALK, SOL_AX25 as SOL_AX25, SOL_HCI as SOL_HCI, @@ -221,15 +393,59 @@ if sys.platform != "win32" and sys.platform != "darwin": TCP_WINDOW_CLAMP as TCP_WINDOW_CLAMP, ) + __all__ += [ + "IP_TRANSPARENT", + "SCM_CREDENTIALS", + "SO_BINDTODEVICE", + "SO_DOMAIN", + "SO_MARK", + "SO_PASSCRED", + "SO_PASSSEC", + "SO_PEERCRED", + "SO_PEERSEC", + "SO_PRIORITY", + "SO_PROTOCOL", + "TCP_CONGESTION", + "TCP_CORK", + "TCP_DEFER_ACCEPT", + "TCP_INFO", + "TCP_LINGER2", + "TCP_QUICKACK", + "TCP_SYNCNT", + "TCP_USER_TIMEOUT", + "TCP_WINDOW_CLAMP", + "AF_ASH", + "AF_ATMPVC", + "AF_ATMSVC", + "AF_AX25", + "AF_BRIDGE", + "AF_ECONET", + "AF_KEY", + "AF_LLC", + "AF_NETBEUI", + "AF_NETROM", + "AF_PPPOX", + "AF_ROSE", + "AF_SECURITY", + "AF_WANPIPE", + "AF_X25", + "MSG_CMSG_CLOEXEC", + "MSG_CONFIRM", + "MSG_FASTOPEN", + "MSG_MORE", + ] + +if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11): + from _socket import IP_BIND_ADDRESS_NO_PORT as IP_BIND_ADDRESS_NO_PORT + + __all__ += ["IP_BIND_ADDRESS_NO_PORT"] + if sys.platform != "win32": from _socket import ( CMSG_LEN as CMSG_LEN, CMSG_SPACE as CMSG_SPACE, EAI_ADDRFAMILY as EAI_ADDRFAMILY, - EAI_BADHINTS as EAI_BADHINTS, - EAI_MAX as EAI_MAX, EAI_OVERFLOW as EAI_OVERFLOW, - EAI_PROTOCOL as EAI_PROTOCOL, EAI_SYSTEM as EAI_SYSTEM, IP_DEFAULT_MULTICAST_LOOP as IP_DEFAULT_MULTICAST_LOOP, IP_DEFAULT_MULTICAST_TTL as IP_DEFAULT_MULTICAST_TTL, @@ -237,23 +453,45 @@ if sys.platform != "win32": IP_RECVOPTS as IP_RECVOPTS, IP_RECVRETOPTS as IP_RECVRETOPTS, IP_RETOPTS as IP_RETOPTS, - IPPROTO_EON as IPPROTO_EON, IPPROTO_GRE as IPPROTO_GRE, - IPPROTO_HELLO as IPPROTO_HELLO, - IPPROTO_IPCOMP as IPPROTO_IPCOMP, IPPROTO_IPIP as IPPROTO_IPIP, IPPROTO_RSVP as IPPROTO_RSVP, IPPROTO_TP as IPPROTO_TP, - IPPROTO_XTP as IPPROTO_XTP, IPV6_RTHDR_TYPE_0 as IPV6_RTHDR_TYPE_0, - LOCAL_PEERCRED as LOCAL_PEERCRED, - SCM_CREDS as SCM_CREDS, SCM_RIGHTS as SCM_RIGHTS, SO_REUSEPORT as SO_REUSEPORT, TCP_NOTSENT_LOWAT as TCP_NOTSENT_LOWAT, sethostname as sethostname, ) + __all__ += [ + "CMSG_LEN", + "CMSG_SPACE", + "EAI_ADDRFAMILY", + "EAI_OVERFLOW", + "EAI_SYSTEM", + "IP_DEFAULT_MULTICAST_LOOP", + "IP_DEFAULT_MULTICAST_TTL", + "IP_MAX_MEMBERSHIPS", + "IP_RECVOPTS", + "IP_RECVRETOPTS", + "IP_RETOPTS", + "IPPROTO_GRE", + "IPPROTO_IPIP", + "IPPROTO_RSVP", + "IPPROTO_TP", + "IPV6_RTHDR_TYPE_0", + "SCM_RIGHTS", + "SO_REUSEPORT", + "TCP_NOTSENT_LOWAT", + "sethostname", + "AF_ROUTE", + "AF_UNIX", + "MSG_DONTWAIT", + "MSG_EOR", + "MSG_NOSIGNAL", + ] + if sys.platform != "darwin" or sys.version_info >= (3, 9): from _socket import ( IPV6_DSTOPTS as IPV6_DSTOPTS, @@ -265,19 +503,36 @@ if sys.platform != "win32": IPV6_RECVPATHMTU as IPV6_RECVPATHMTU, IPV6_RECVPKTINFO as IPV6_RECVPKTINFO, IPV6_RTHDRDSTOPTS as IPV6_RTHDRDSTOPTS, - IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU, ) -if sys.platform != "darwin": + __all__ += [ + "IPV6_DSTOPTS", + "IPV6_NEXTHOP", + "IPV6_PATHMTU", + "IPV6_RECVDSTOPTS", + "IPV6_RECVHOPLIMIT", + "IPV6_RECVHOPOPTS", + "IPV6_RECVPATHMTU", + "IPV6_RECVPKTINFO", + "IPV6_RTHDRDSTOPTS", + ] + +if sys.platform != "darwin" and sys.platform != "linux": if sys.platform != "win32" or sys.version_info >= (3, 9): from _socket import BDADDR_ANY as BDADDR_ANY, BDADDR_LOCAL as BDADDR_LOCAL, BTPROTO_RFCOMM as BTPROTO_RFCOMM + __all__ += ["BDADDR_ANY", "BDADDR_LOCAL", "BTPROTO_RFCOMM"] + if sys.platform == "darwin" and sys.version_info >= (3, 10): from _socket import TCP_KEEPALIVE as TCP_KEEPALIVE + __all__ += ["TCP_KEEPALIVE"] + if sys.platform == "darwin" and sys.version_info >= (3, 11): from _socket import TCP_CONNECTION_INFO as TCP_CONNECTION_INFO + __all__ += ["TCP_CONNECTION_INFO"] + if sys.platform == "linux": from _socket import ( ALG_OP_DECRYPT as ALG_OP_DECRYPT, @@ -321,7 +576,6 @@ if sys.platform == "linux": CAN_ERR_MASK as CAN_ERR_MASK, CAN_ISOTP as CAN_ISOTP, CAN_RAW as CAN_RAW, - CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER, CAN_RAW_FD_FRAMES as CAN_RAW_FD_FRAMES, CAN_RAW_FILTER as CAN_RAW_FILTER, CAN_RAW_LOOPBACK as CAN_RAW_LOOPBACK, @@ -329,19 +583,13 @@ if sys.platform == "linux": CAN_RTR_FLAG as CAN_RTR_FLAG, CAN_SFF_MASK as CAN_SFF_MASK, IOCTL_VM_SOCKETS_GET_LOCAL_CID as IOCTL_VM_SOCKETS_GET_LOCAL_CID, - NETLINK_ARPD as NETLINK_ARPD, NETLINK_CRYPTO as NETLINK_CRYPTO, NETLINK_DNRTMSG as NETLINK_DNRTMSG, NETLINK_FIREWALL as NETLINK_FIREWALL, NETLINK_IP6_FW as NETLINK_IP6_FW, NETLINK_NFLOG as NETLINK_NFLOG, NETLINK_ROUTE as NETLINK_ROUTE, - NETLINK_ROUTE6 as NETLINK_ROUTE6, - NETLINK_SKIP as NETLINK_SKIP, - NETLINK_TAPBASE as NETLINK_TAPBASE, - NETLINK_TCPDIAG as NETLINK_TCPDIAG, NETLINK_USERSOCK as NETLINK_USERSOCK, - NETLINK_W1 as NETLINK_W1, NETLINK_XFRM as NETLINK_XFRM, PACKET_BROADCAST as PACKET_BROADCAST, PACKET_FASTROUTE as PACKET_FASTROUTE, @@ -358,7 +606,6 @@ if sys.platform == "linux": RDS_CMSG_RDMA_DEST as RDS_CMSG_RDMA_DEST, RDS_CMSG_RDMA_MAP as RDS_CMSG_RDMA_MAP, RDS_CMSG_RDMA_STATUS as RDS_CMSG_RDMA_STATUS, - RDS_CMSG_RDMA_UPDATE as RDS_CMSG_RDMA_UPDATE, RDS_CONG_MONITOR as RDS_CONG_MONITOR, RDS_FREE_MR as RDS_FREE_MR, RDS_GET_MR as RDS_GET_MR, @@ -408,10 +655,130 @@ if sys.platform == "linux": VMADDR_PORT_ANY as VMADDR_PORT_ANY, ) + __all__ += [ + "ALG_OP_DECRYPT", + "ALG_OP_ENCRYPT", + "ALG_OP_SIGN", + "ALG_OP_VERIFY", + "ALG_SET_AEAD_ASSOCLEN", + "ALG_SET_AEAD_AUTHSIZE", + "ALG_SET_IV", + "ALG_SET_KEY", + "ALG_SET_OP", + "ALG_SET_PUBKEY", + "CAN_BCM", + "CAN_BCM_CAN_FD_FRAME", + "CAN_BCM_RX_ANNOUNCE_RESUME", + "CAN_BCM_RX_CHANGED", + "CAN_BCM_RX_CHECK_DLC", + "CAN_BCM_RX_DELETE", + "CAN_BCM_RX_FILTER_ID", + "CAN_BCM_RX_NO_AUTOTIMER", + "CAN_BCM_RX_READ", + "CAN_BCM_RX_RTR_FRAME", + "CAN_BCM_RX_SETUP", + "CAN_BCM_RX_STATUS", + "CAN_BCM_RX_TIMEOUT", + "CAN_BCM_SETTIMER", + "CAN_BCM_STARTTIMER", + "CAN_BCM_TX_ANNOUNCE", + "CAN_BCM_TX_COUNTEVT", + "CAN_BCM_TX_CP_CAN_ID", + "CAN_BCM_TX_DELETE", + "CAN_BCM_TX_EXPIRED", + "CAN_BCM_TX_READ", + "CAN_BCM_TX_RESET_MULTI_IDX", + "CAN_BCM_TX_SEND", + "CAN_BCM_TX_SETUP", + "CAN_BCM_TX_STATUS", + "CAN_EFF_FLAG", + "CAN_EFF_MASK", + "CAN_ERR_FLAG", + "CAN_ERR_MASK", + "CAN_ISOTP", + "CAN_RAW", + "CAN_RAW_FD_FRAMES", + "CAN_RAW_FILTER", + "CAN_RAW_LOOPBACK", + "CAN_RAW_RECV_OWN_MSGS", + "CAN_RTR_FLAG", + "CAN_SFF_MASK", + "IOCTL_VM_SOCKETS_GET_LOCAL_CID", + "NETLINK_CRYPTO", + "NETLINK_DNRTMSG", + "NETLINK_FIREWALL", + "NETLINK_IP6_FW", + "NETLINK_NFLOG", + "NETLINK_ROUTE", + "NETLINK_USERSOCK", + "NETLINK_XFRM", + "PACKET_BROADCAST", + "PACKET_FASTROUTE", + "PACKET_HOST", + "PACKET_LOOPBACK", + "PACKET_MULTICAST", + "PACKET_OTHERHOST", + "PACKET_OUTGOING", + "PF_CAN", + "PF_PACKET", + "PF_RDS", + "SO_VM_SOCKETS_BUFFER_MAX_SIZE", + "SO_VM_SOCKETS_BUFFER_MIN_SIZE", + "SO_VM_SOCKETS_BUFFER_SIZE", + "SOL_ALG", + "SOL_CAN_BASE", + "SOL_CAN_RAW", + "SOL_RDS", + "SOL_TIPC", + "TIPC_ADDR_ID", + "TIPC_ADDR_NAME", + "TIPC_ADDR_NAMESEQ", + "TIPC_CFG_SRV", + "TIPC_CLUSTER_SCOPE", + "TIPC_CONN_TIMEOUT", + "TIPC_CRITICAL_IMPORTANCE", + "TIPC_DEST_DROPPABLE", + "TIPC_HIGH_IMPORTANCE", + "TIPC_IMPORTANCE", + "TIPC_LOW_IMPORTANCE", + "TIPC_MEDIUM_IMPORTANCE", + "TIPC_NODE_SCOPE", + "TIPC_PUBLISHED", + "TIPC_SRC_DROPPABLE", + "TIPC_SUB_CANCEL", + "TIPC_SUB_PORTS", + "TIPC_SUB_SERVICE", + "TIPC_SUBSCR_TIMEOUT", + "TIPC_TOP_SRV", + "TIPC_WAIT_FOREVER", + "TIPC_WITHDRAWN", + "TIPC_ZONE_SCOPE", + "VM_SOCKETS_INVALID_VERSION", + "VMADDR_CID_ANY", + "VMADDR_CID_HOST", + "VMADDR_PORT_ANY", + "AF_CAN", + "AF_PACKET", + "AF_RDS", + "AF_TIPC", + "AF_ALG", + "AF_NETLINK", + "AF_VSOCK", + "AF_QIPCRTR", + "SOCK_CLOEXEC", + "SOCK_NONBLOCK", + ] + + if sys.version_info < (3, 11): + from _socket import CAN_RAW_ERR_FILTER as CAN_RAW_ERR_FILTER + + __all__ += ["CAN_RAW_ERR_FILTER"] + if sys.platform == "linux" and sys.version_info >= (3, 9): from _socket import ( CAN_J1939 as CAN_J1939, CAN_RAW_JOIN_FILTERS as CAN_RAW_JOIN_FILTERS, + IPPROTO_UDPLITE as IPPROTO_UDPLITE, J1939_EE_INFO_NONE as J1939_EE_INFO_NONE, J1939_EE_INFO_TX_ABORT as J1939_EE_INFO_TX_ABORT, J1939_FILTER_MAX as J1939_FILTER_MAX, @@ -438,11 +805,97 @@ if sys.platform == "linux" and sys.version_info >= (3, 9): UDPLITE_RECV_CSCOV as UDPLITE_RECV_CSCOV, UDPLITE_SEND_CSCOV as UDPLITE_SEND_CSCOV, ) + + __all__ += [ + "CAN_J1939", + "CAN_RAW_JOIN_FILTERS", + "IPPROTO_UDPLITE", + "J1939_EE_INFO_NONE", + "J1939_EE_INFO_TX_ABORT", + "J1939_FILTER_MAX", + "J1939_IDLE_ADDR", + "J1939_MAX_UNICAST_ADDR", + "J1939_NLA_BYTES_ACKED", + "J1939_NLA_PAD", + "J1939_NO_ADDR", + "J1939_NO_NAME", + "J1939_NO_PGN", + "J1939_PGN_ADDRESS_CLAIMED", + "J1939_PGN_ADDRESS_COMMANDED", + "J1939_PGN_MAX", + "J1939_PGN_PDU1_MAX", + "J1939_PGN_REQUEST", + "SCM_J1939_DEST_ADDR", + "SCM_J1939_DEST_NAME", + "SCM_J1939_ERRQUEUE", + "SCM_J1939_PRIO", + "SO_J1939_ERRQUEUE", + "SO_J1939_FILTER", + "SO_J1939_PROMISC", + "SO_J1939_SEND_PRIO", + "UDPLITE_RECV_CSCOV", + "UDPLITE_SEND_CSCOV", + ] if sys.platform == "linux" and sys.version_info >= (3, 10): from _socket import IPPROTO_MPTCP as IPPROTO_MPTCP + + __all__ += ["IPPROTO_MPTCP"] if sys.platform == "linux" and sys.version_info >= (3, 11): from _socket import SO_INCOMING_CPU as SO_INCOMING_CPU + __all__ += ["SO_INCOMING_CPU"] +if sys.platform == "linux" and sys.version_info >= (3, 12): + from _socket import ( + TCP_CC_INFO as TCP_CC_INFO, + TCP_FASTOPEN_CONNECT as TCP_FASTOPEN_CONNECT, + TCP_FASTOPEN_KEY as TCP_FASTOPEN_KEY, + TCP_FASTOPEN_NO_COOKIE as TCP_FASTOPEN_NO_COOKIE, + TCP_INQ as TCP_INQ, + TCP_MD5SIG as TCP_MD5SIG, + TCP_MD5SIG_EXT as TCP_MD5SIG_EXT, + TCP_QUEUE_SEQ as TCP_QUEUE_SEQ, + TCP_REPAIR as TCP_REPAIR, + TCP_REPAIR_OPTIONS as TCP_REPAIR_OPTIONS, + TCP_REPAIR_QUEUE as TCP_REPAIR_QUEUE, + TCP_REPAIR_WINDOW as TCP_REPAIR_WINDOW, + TCP_SAVE_SYN as TCP_SAVE_SYN, + TCP_SAVED_SYN as TCP_SAVED_SYN, + TCP_THIN_DUPACK as TCP_THIN_DUPACK, + TCP_THIN_LINEAR_TIMEOUTS as TCP_THIN_LINEAR_TIMEOUTS, + TCP_TIMESTAMP as TCP_TIMESTAMP, + TCP_TX_DELAY as TCP_TX_DELAY, + TCP_ULP as TCP_ULP, + TCP_ZEROCOPY_RECEIVE as TCP_ZEROCOPY_RECEIVE, + ) + + __all__ += [ + "TCP_CC_INFO", + "TCP_FASTOPEN_CONNECT", + "TCP_FASTOPEN_KEY", + "TCP_FASTOPEN_NO_COOKIE", + "TCP_INQ", + "TCP_MD5SIG", + "TCP_MD5SIG_EXT", + "TCP_QUEUE_SEQ", + "TCP_REPAIR", + "TCP_REPAIR_OPTIONS", + "TCP_REPAIR_QUEUE", + "TCP_REPAIR_WINDOW", + "TCP_SAVED_SYN", + "TCP_SAVE_SYN", + "TCP_THIN_DUPACK", + "TCP_THIN_LINEAR_TIMEOUTS", + "TCP_TIMESTAMP", + "TCP_TX_DELAY", + "TCP_ULP", + "TCP_ZEROCOPY_RECEIVE", + ] + +if sys.platform == "linux" and sys.version_info >= (3, 13): + from _socket import NI_IDN as NI_IDN, SO_BINDTOIFINDEX as SO_BINDTOIFINDEX + + __all__ += ["NI_IDN", "SO_BINDTOIFINDEX"] + if sys.version_info >= (3, 12): from _socket import ( IP_ADD_SOURCE_MEMBERSHIP as IP_ADD_SOURCE_MEMBERSHIP, @@ -452,6 +905,8 @@ if sys.version_info >= (3, 12): IP_UNBLOCK_SOURCE as IP_UNBLOCK_SOURCE, ) + __all__ += ["IP_ADD_SOURCE_MEMBERSHIP", "IP_BLOCK_SOURCE", "IP_DROP_SOURCE_MEMBERSHIP", "IP_PKTINFO", "IP_UNBLOCK_SOURCE"] + if sys.platform == "win32": from _socket import ( HV_GUID_BROADCAST as HV_GUID_BROADCAST, @@ -466,6 +921,20 @@ if sys.version_info >= (3, 12): HVSOCKET_CONNECT_TIMEOUT_MAX as HVSOCKET_CONNECT_TIMEOUT_MAX, HVSOCKET_CONNECTED_SUSPEND as HVSOCKET_CONNECTED_SUSPEND, ) + + __all__ += [ + "HV_GUID_BROADCAST", + "HV_GUID_CHILDREN", + "HV_GUID_LOOPBACK", + "HV_GUID_PARENT", + "HV_GUID_WILDCARD", + "HV_GUID_ZERO", + "HV_PROTOCOL_RAW", + "HVSOCKET_ADDRESS_FLAG_PASSTHRU", + "HVSOCKET_CONNECT_TIMEOUT", + "HVSOCKET_CONNECT_TIMEOUT_MAX", + "HVSOCKET_CONNECTED_SUSPEND", + ] else: from _socket import ( ETHERTYPE_ARP as ETHERTYPE_ARP, @@ -474,18 +943,105 @@ if sys.version_info >= (3, 12): ETHERTYPE_VLAN as ETHERTYPE_VLAN, ) + __all__ += ["ETHERTYPE_ARP", "ETHERTYPE_IP", "ETHERTYPE_IPV6", "ETHERTYPE_VLAN"] + if sys.platform == "linux": from _socket import ETH_P_ALL as ETH_P_ALL + __all__ += ["ETH_P_ALL"] + if sys.platform != "linux" and sys.platform != "win32" and sys.platform != "darwin": # FreeBSD >= 14.0 from _socket import PF_DIVERT as PF_DIVERT + __all__ += ["PF_DIVERT", "AF_DIVERT"] + +if sys.platform != "win32" and sys.version_info >= (3, 9): + __all__ += ["send_fds", "recv_fds"] + +if sys.platform != "win32" or sys.version_info >= (3, 9): + if sys.platform != "linux": + __all__ += ["AF_LINK"] + if sys.platform != "darwin" and sys.platform != "linux": + __all__ += ["AF_BLUETOOTH"] + +if sys.platform == "win32" and sys.version_info >= (3, 12): + __all__ += ["AF_HYPERV"] + +if sys.platform != "win32" and sys.platform != "linux": + from _socket import ( + EAI_BADHINTS as EAI_BADHINTS, + EAI_MAX as EAI_MAX, + EAI_PROTOCOL as EAI_PROTOCOL, + IPPROTO_EON as IPPROTO_EON, + IPPROTO_HELLO as IPPROTO_HELLO, + IPPROTO_IPCOMP as IPPROTO_IPCOMP, + IPPROTO_XTP as IPPROTO_XTP, + LOCAL_PEERCRED as LOCAL_PEERCRED, + SCM_CREDS as SCM_CREDS, + ) + + __all__ += [ + "EAI_BADHINTS", + "EAI_MAX", + "EAI_PROTOCOL", + "IPPROTO_EON", + "IPPROTO_HELLO", + "IPPROTO_IPCOMP", + "IPPROTO_XTP", + "LOCAL_PEERCRED", + "SCM_CREDS", + "AI_DEFAULT", + "AI_MASK", + "AI_V4MAPPED_CFG", + "MSG_EOF", + ] + if sys.platform != "darwin" or sys.version_info >= (3, 9): + from _socket import IPV6_USE_MIN_MTU as IPV6_USE_MIN_MTU + + __all__ += ["IPV6_USE_MIN_MTU"] + +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + from _socket import ( + IPPROTO_BIP as IPPROTO_BIP, + IPPROTO_MOBILE as IPPROTO_MOBILE, + IPPROTO_VRRP as IPPROTO_VRRP, + MSG_BTAG as MSG_BTAG, + MSG_ETAG as MSG_ETAG, + SO_SETFIB as SO_SETFIB, + ) + + __all__ += ["SO_SETFIB", "MSG_BTAG", "MSG_ETAG", "IPPROTO_BIP", "IPPROTO_MOBILE", "IPPROTO_VRRP", "MSG_NOTIFICATION"] + +if sys.platform != "linux": + from _socket import ( + IP_RECVDSTADDR as IP_RECVDSTADDR, + IPPROTO_GGP as IPPROTO_GGP, + IPPROTO_IPV4 as IPPROTO_IPV4, + IPPROTO_MAX as IPPROTO_MAX, + IPPROTO_ND as IPPROTO_ND, + SO_USELOOPBACK as SO_USELOOPBACK, + ) + + __all__ += ["IPPROTO_GGP", "IPPROTO_IPV4", "IPPROTO_MAX", "IPPROTO_ND", "IP_RECVDSTADDR", "SO_USELOOPBACK"] + # Re-exported from errno EBADF: int EAGAIN: int EWOULDBLOCK: int +# These errors are implemented in _socket at runtime +# but they consider themselves to live in socket so we'll put them here. +error = OSError + +class herror(error): ... +class gaierror(error): ... + +if sys.version_info >= (3, 10): + timeout = TimeoutError +else: + class timeout(error): ... + class AddressFamily(IntEnum): AF_INET = 2 AF_INET6 = 10 @@ -498,10 +1054,10 @@ class AddressFamily(IntEnum): AF_IRDA = 23 if sys.platform != "win32": AF_ROUTE = 16 - AF_SYSTEM = 32 AF_UNIX = 1 + if sys.platform == "darwin": + AF_SYSTEM = 32 if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5 = ... AF_ASH = 18 AF_ATMPVC = 8 AF_ATMSVC = 20 @@ -527,8 +1083,9 @@ class AddressFamily(IntEnum): AF_VSOCK = 40 AF_QIPCRTR = 42 if sys.platform != "win32" or sys.version_info >= (3, 9): - AF_LINK = 33 - if sys.platform != "darwin": + if sys.platform != "linux": + AF_LINK = 33 + if sys.platform != "darwin" and sys.platform != "linux": AF_BLUETOOTH = 32 if sys.platform == "win32" and sys.version_info >= (3, 12): AF_HYPERV = 34 @@ -549,11 +1106,12 @@ if sys.platform != "darwin": if sys.platform != "win32": AF_ROUTE = AddressFamily.AF_ROUTE - AF_SYSTEM = AddressFamily.AF_SYSTEM AF_UNIX = AddressFamily.AF_UNIX +if sys.platform == "darwin": + AF_SYSTEM = AddressFamily.AF_SYSTEM + if sys.platform != "win32" and sys.platform != "darwin": - AF_AAL5 = AddressFamily.AF_AAL5 AF_ASH = AddressFamily.AF_ASH AF_ATMPVC = AddressFamily.AF_ATMPVC AF_ATMSVC = AddressFamily.AF_ATMSVC @@ -581,8 +1139,9 @@ if sys.platform == "linux": AF_QIPCRTR = AddressFamily.AF_QIPCRTR if sys.platform != "win32" or sys.version_info >= (3, 9): - AF_LINK = AddressFamily.AF_LINK - if sys.platform != "darwin": + if sys.platform != "linux": + AF_LINK = AddressFamily.AF_LINK + if sys.platform != "darwin" and sys.platform != "linux": AF_BLUETOOTH = AddressFamily.AF_BLUETOOTH if sys.platform == "win32" and sys.version_info >= (3, 12): @@ -617,26 +1176,28 @@ class MsgFlag(IntFlag): MSG_PEEK = 2 MSG_TRUNC = 32 MSG_WAITALL = 256 - - if sys.platform != "darwin": + if sys.platform == "win32": MSG_BCAST = 1024 MSG_MCAST = 2048 + + if sys.platform != "darwin": MSG_ERRQUEUE = 8192 if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG = ... MSG_CMSG_CLOEXEC = 1073741821 MSG_CONFIRM = 2048 - MSG_ETAG = ... MSG_FASTOPEN = 536870912 MSG_MORE = 32768 - MSG_NOTIFICATION = ... + + if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": + MSG_NOTIFICATION = 8192 if sys.platform != "win32": MSG_DONTWAIT = 64 - MSG_EOF = 256 MSG_EOR = 128 MSG_NOSIGNAL = 16384 # sometimes this exists on darwin, sometimes not + if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF = 256 MSG_CTRUNC = MsgFlag.MSG_CTRUNC MSG_DONTROUTE = MsgFlag.MSG_DONTROUTE @@ -645,26 +1206,30 @@ MSG_PEEK = MsgFlag.MSG_PEEK MSG_TRUNC = MsgFlag.MSG_TRUNC MSG_WAITALL = MsgFlag.MSG_WAITALL -if sys.platform != "darwin": +if sys.platform == "win32": MSG_BCAST = MsgFlag.MSG_BCAST MSG_MCAST = MsgFlag.MSG_MCAST + +if sys.platform != "darwin": MSG_ERRQUEUE = MsgFlag.MSG_ERRQUEUE if sys.platform != "win32": MSG_DONTWAIT = MsgFlag.MSG_DONTWAIT - MSG_EOF = MsgFlag.MSG_EOF MSG_EOR = MsgFlag.MSG_EOR MSG_NOSIGNAL = MsgFlag.MSG_NOSIGNAL # Sometimes this exists on darwin, sometimes not if sys.platform != "win32" and sys.platform != "darwin": - MSG_BTAG = MsgFlag.MSG_BTAG MSG_CMSG_CLOEXEC = MsgFlag.MSG_CMSG_CLOEXEC MSG_CONFIRM = MsgFlag.MSG_CONFIRM - MSG_ETAG = MsgFlag.MSG_ETAG MSG_FASTOPEN = MsgFlag.MSG_FASTOPEN MSG_MORE = MsgFlag.MSG_MORE + +if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux": MSG_NOTIFICATION = MsgFlag.MSG_NOTIFICATION +if sys.platform != "win32" and sys.platform != "linux": + MSG_EOF = MsgFlag.MSG_EOF + class AddressInfo(IntFlag): AI_ADDRCONFIG = 32 AI_ALL = 16 @@ -673,7 +1238,7 @@ class AddressInfo(IntFlag): AI_NUMERICSERV = 1024 AI_PASSIVE = 1 AI_V4MAPPED = 8 - if sys.platform != "win32": + if sys.platform != "win32" and sys.platform != "linux": AI_DEFAULT = 1536 AI_MASK = 5127 AI_V4MAPPED_CFG = 512 @@ -686,7 +1251,7 @@ AI_NUMERICSERV = AddressInfo.AI_NUMERICSERV AI_PASSIVE = AddressInfo.AI_PASSIVE AI_V4MAPPED = AddressInfo.AI_V4MAPPED -if sys.platform != "win32": +if sys.platform != "win32" and sys.platform != "linux": AI_DEFAULT = AddressInfo.AI_DEFAULT AI_MASK = AddressInfo.AI_MASK AI_V4MAPPED_CFG = AddressInfo.AI_V4MAPPED_CFG @@ -710,7 +1275,7 @@ class socket(_socket.socket): ) -> None: ... def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... - def dup(self) -> Self: ... # noqa: F811 + def dup(self) -> Self: ... def accept(self) -> tuple[socket, _RetAddress]: ... # Note that the makefile's documented windows-specific behavior is not represented # mode strings with duplicates are intentionally excluded diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi index 5753d1d661b91..ae6575d850822 100644 --- a/mypy/typeshed/stdlib/socketserver.pyi +++ b/mypy/typeshed/stdlib/socketserver.pyi @@ -3,8 +3,9 @@ import types from _socket import _Address, _RetAddress from _typeshed import ReadableBuffer from collections.abc import Callable +from io import BufferedIOBase from socket import socket as _socket -from typing import Any, BinaryIO, ClassVar +from typing import Any, ClassVar from typing_extensions import Self, TypeAlias __all__ = [ @@ -158,11 +159,11 @@ class StreamRequestHandler(BaseRequestHandler): timeout: ClassVar[float | None] # undocumented disable_nagle_algorithm: ClassVar[bool] # undocumented connection: Any # undocumented - rfile: BinaryIO - wfile: BinaryIO + rfile: BufferedIOBase + wfile: BufferedIOBase class DatagramRequestHandler(BaseRequestHandler): - packet: _socket # undocumented + packet: bytes # undocumented socket: _socket # undocumented - rfile: BinaryIO - wfile: BinaryIO + rfile: BufferedIOBase + wfile: BufferedIOBase diff --git a/mypy/typeshed/stdlib/sqlite3/__init__.pyi b/mypy/typeshed/stdlib/sqlite3/__init__.pyi index d747be90fd0a5..730404bde218e 100644 --- a/mypy/typeshed/stdlib/sqlite3/__init__.pyi +++ b/mypy/typeshed/stdlib/sqlite3/__init__.pyi @@ -1 +1,467 @@ -from sqlite3.dbapi2 import * +import sys +from _typeshed import MaybeNone, ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused +from collections.abc import Callable, Generator, Iterable, Iterator, Mapping, Sequence +from sqlite3.dbapi2 import ( + PARSE_COLNAMES as PARSE_COLNAMES, + PARSE_DECLTYPES as PARSE_DECLTYPES, + SQLITE_ALTER_TABLE as SQLITE_ALTER_TABLE, + SQLITE_ANALYZE as SQLITE_ANALYZE, + SQLITE_ATTACH as SQLITE_ATTACH, + SQLITE_CREATE_INDEX as SQLITE_CREATE_INDEX, + SQLITE_CREATE_TABLE as SQLITE_CREATE_TABLE, + SQLITE_CREATE_TEMP_INDEX as SQLITE_CREATE_TEMP_INDEX, + SQLITE_CREATE_TEMP_TABLE as SQLITE_CREATE_TEMP_TABLE, + SQLITE_CREATE_TEMP_TRIGGER as SQLITE_CREATE_TEMP_TRIGGER, + SQLITE_CREATE_TEMP_VIEW as SQLITE_CREATE_TEMP_VIEW, + SQLITE_CREATE_TRIGGER as SQLITE_CREATE_TRIGGER, + SQLITE_CREATE_VIEW as SQLITE_CREATE_VIEW, + SQLITE_CREATE_VTABLE as SQLITE_CREATE_VTABLE, + SQLITE_DELETE as SQLITE_DELETE, + SQLITE_DENY as SQLITE_DENY, + SQLITE_DETACH as SQLITE_DETACH, + SQLITE_DONE as SQLITE_DONE, + SQLITE_DROP_INDEX as SQLITE_DROP_INDEX, + SQLITE_DROP_TABLE as SQLITE_DROP_TABLE, + SQLITE_DROP_TEMP_INDEX as SQLITE_DROP_TEMP_INDEX, + SQLITE_DROP_TEMP_TABLE as SQLITE_DROP_TEMP_TABLE, + SQLITE_DROP_TEMP_TRIGGER as SQLITE_DROP_TEMP_TRIGGER, + SQLITE_DROP_TEMP_VIEW as SQLITE_DROP_TEMP_VIEW, + SQLITE_DROP_TRIGGER as SQLITE_DROP_TRIGGER, + SQLITE_DROP_VIEW as SQLITE_DROP_VIEW, + SQLITE_DROP_VTABLE as SQLITE_DROP_VTABLE, + SQLITE_FUNCTION as SQLITE_FUNCTION, + SQLITE_IGNORE as SQLITE_IGNORE, + SQLITE_INSERT as SQLITE_INSERT, + SQLITE_OK as SQLITE_OK, + SQLITE_PRAGMA as SQLITE_PRAGMA, + SQLITE_READ as SQLITE_READ, + SQLITE_RECURSIVE as SQLITE_RECURSIVE, + SQLITE_REINDEX as SQLITE_REINDEX, + SQLITE_SAVEPOINT as SQLITE_SAVEPOINT, + SQLITE_SELECT as SQLITE_SELECT, + SQLITE_TRANSACTION as SQLITE_TRANSACTION, + SQLITE_UPDATE as SQLITE_UPDATE, + Binary as Binary, + Date as Date, + DateFromTicks as DateFromTicks, + Time as Time, + TimeFromTicks as TimeFromTicks, + TimestampFromTicks as TimestampFromTicks, + adapt as adapt, + adapters as adapters, + apilevel as apilevel, + complete_statement as complete_statement, + connect as connect, + converters as converters, + enable_callback_tracebacks as enable_callback_tracebacks, + paramstyle as paramstyle, + register_adapter as register_adapter, + register_converter as register_converter, + sqlite_version as sqlite_version, + sqlite_version_info as sqlite_version_info, + threadsafety as threadsafety, + version_info as version_info, +) +from types import TracebackType +from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload, type_check_only +from typing_extensions import Self, TypeAlias + +if sys.version_info >= (3, 12): + from sqlite3.dbapi2 import ( + LEGACY_TRANSACTION_CONTROL as LEGACY_TRANSACTION_CONTROL, + SQLITE_DBCONFIG_DEFENSIVE as SQLITE_DBCONFIG_DEFENSIVE, + SQLITE_DBCONFIG_DQS_DDL as SQLITE_DBCONFIG_DQS_DDL, + SQLITE_DBCONFIG_DQS_DML as SQLITE_DBCONFIG_DQS_DML, + SQLITE_DBCONFIG_ENABLE_FKEY as SQLITE_DBCONFIG_ENABLE_FKEY, + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER as SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION as SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, + SQLITE_DBCONFIG_ENABLE_QPSG as SQLITE_DBCONFIG_ENABLE_QPSG, + SQLITE_DBCONFIG_ENABLE_TRIGGER as SQLITE_DBCONFIG_ENABLE_TRIGGER, + SQLITE_DBCONFIG_ENABLE_VIEW as SQLITE_DBCONFIG_ENABLE_VIEW, + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE as SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT as SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE as SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, + SQLITE_DBCONFIG_RESET_DATABASE as SQLITE_DBCONFIG_RESET_DATABASE, + SQLITE_DBCONFIG_TRIGGER_EQP as SQLITE_DBCONFIG_TRIGGER_EQP, + SQLITE_DBCONFIG_TRUSTED_SCHEMA as SQLITE_DBCONFIG_TRUSTED_SCHEMA, + SQLITE_DBCONFIG_WRITABLE_SCHEMA as SQLITE_DBCONFIG_WRITABLE_SCHEMA, + ) + +if sys.version_info >= (3, 11): + from sqlite3.dbapi2 import ( + SQLITE_ABORT as SQLITE_ABORT, + SQLITE_ABORT_ROLLBACK as SQLITE_ABORT_ROLLBACK, + SQLITE_AUTH as SQLITE_AUTH, + SQLITE_AUTH_USER as SQLITE_AUTH_USER, + SQLITE_BUSY as SQLITE_BUSY, + SQLITE_BUSY_RECOVERY as SQLITE_BUSY_RECOVERY, + SQLITE_BUSY_SNAPSHOT as SQLITE_BUSY_SNAPSHOT, + SQLITE_BUSY_TIMEOUT as SQLITE_BUSY_TIMEOUT, + SQLITE_CANTOPEN as SQLITE_CANTOPEN, + SQLITE_CANTOPEN_CONVPATH as SQLITE_CANTOPEN_CONVPATH, + SQLITE_CANTOPEN_DIRTYWAL as SQLITE_CANTOPEN_DIRTYWAL, + SQLITE_CANTOPEN_FULLPATH as SQLITE_CANTOPEN_FULLPATH, + SQLITE_CANTOPEN_ISDIR as SQLITE_CANTOPEN_ISDIR, + SQLITE_CANTOPEN_NOTEMPDIR as SQLITE_CANTOPEN_NOTEMPDIR, + SQLITE_CANTOPEN_SYMLINK as SQLITE_CANTOPEN_SYMLINK, + SQLITE_CONSTRAINT as SQLITE_CONSTRAINT, + SQLITE_CONSTRAINT_CHECK as SQLITE_CONSTRAINT_CHECK, + SQLITE_CONSTRAINT_COMMITHOOK as SQLITE_CONSTRAINT_COMMITHOOK, + SQLITE_CONSTRAINT_FOREIGNKEY as SQLITE_CONSTRAINT_FOREIGNKEY, + SQLITE_CONSTRAINT_FUNCTION as SQLITE_CONSTRAINT_FUNCTION, + SQLITE_CONSTRAINT_NOTNULL as SQLITE_CONSTRAINT_NOTNULL, + SQLITE_CONSTRAINT_PINNED as SQLITE_CONSTRAINT_PINNED, + SQLITE_CONSTRAINT_PRIMARYKEY as SQLITE_CONSTRAINT_PRIMARYKEY, + SQLITE_CONSTRAINT_ROWID as SQLITE_CONSTRAINT_ROWID, + SQLITE_CONSTRAINT_TRIGGER as SQLITE_CONSTRAINT_TRIGGER, + SQLITE_CONSTRAINT_UNIQUE as SQLITE_CONSTRAINT_UNIQUE, + SQLITE_CONSTRAINT_VTAB as SQLITE_CONSTRAINT_VTAB, + SQLITE_CORRUPT as SQLITE_CORRUPT, + SQLITE_CORRUPT_INDEX as SQLITE_CORRUPT_INDEX, + SQLITE_CORRUPT_SEQUENCE as SQLITE_CORRUPT_SEQUENCE, + SQLITE_CORRUPT_VTAB as SQLITE_CORRUPT_VTAB, + SQLITE_EMPTY as SQLITE_EMPTY, + SQLITE_ERROR as SQLITE_ERROR, + SQLITE_ERROR_MISSING_COLLSEQ as SQLITE_ERROR_MISSING_COLLSEQ, + SQLITE_ERROR_RETRY as SQLITE_ERROR_RETRY, + SQLITE_ERROR_SNAPSHOT as SQLITE_ERROR_SNAPSHOT, + SQLITE_FORMAT as SQLITE_FORMAT, + SQLITE_FULL as SQLITE_FULL, + SQLITE_INTERNAL as SQLITE_INTERNAL, + SQLITE_INTERRUPT as SQLITE_INTERRUPT, + SQLITE_IOERR as SQLITE_IOERR, + SQLITE_IOERR_ACCESS as SQLITE_IOERR_ACCESS, + SQLITE_IOERR_AUTH as SQLITE_IOERR_AUTH, + SQLITE_IOERR_BEGIN_ATOMIC as SQLITE_IOERR_BEGIN_ATOMIC, + SQLITE_IOERR_BLOCKED as SQLITE_IOERR_BLOCKED, + SQLITE_IOERR_CHECKRESERVEDLOCK as SQLITE_IOERR_CHECKRESERVEDLOCK, + SQLITE_IOERR_CLOSE as SQLITE_IOERR_CLOSE, + SQLITE_IOERR_COMMIT_ATOMIC as SQLITE_IOERR_COMMIT_ATOMIC, + SQLITE_IOERR_CONVPATH as SQLITE_IOERR_CONVPATH, + SQLITE_IOERR_CORRUPTFS as SQLITE_IOERR_CORRUPTFS, + SQLITE_IOERR_DATA as SQLITE_IOERR_DATA, + SQLITE_IOERR_DELETE as SQLITE_IOERR_DELETE, + SQLITE_IOERR_DELETE_NOENT as SQLITE_IOERR_DELETE_NOENT, + SQLITE_IOERR_DIR_CLOSE as SQLITE_IOERR_DIR_CLOSE, + SQLITE_IOERR_DIR_FSYNC as SQLITE_IOERR_DIR_FSYNC, + SQLITE_IOERR_FSTAT as SQLITE_IOERR_FSTAT, + SQLITE_IOERR_FSYNC as SQLITE_IOERR_FSYNC, + SQLITE_IOERR_GETTEMPPATH as SQLITE_IOERR_GETTEMPPATH, + SQLITE_IOERR_LOCK as SQLITE_IOERR_LOCK, + SQLITE_IOERR_MMAP as SQLITE_IOERR_MMAP, + SQLITE_IOERR_NOMEM as SQLITE_IOERR_NOMEM, + SQLITE_IOERR_RDLOCK as SQLITE_IOERR_RDLOCK, + SQLITE_IOERR_READ as SQLITE_IOERR_READ, + SQLITE_IOERR_ROLLBACK_ATOMIC as SQLITE_IOERR_ROLLBACK_ATOMIC, + SQLITE_IOERR_SEEK as SQLITE_IOERR_SEEK, + SQLITE_IOERR_SHMLOCK as SQLITE_IOERR_SHMLOCK, + SQLITE_IOERR_SHMMAP as SQLITE_IOERR_SHMMAP, + SQLITE_IOERR_SHMOPEN as SQLITE_IOERR_SHMOPEN, + SQLITE_IOERR_SHMSIZE as SQLITE_IOERR_SHMSIZE, + SQLITE_IOERR_SHORT_READ as SQLITE_IOERR_SHORT_READ, + SQLITE_IOERR_TRUNCATE as SQLITE_IOERR_TRUNCATE, + SQLITE_IOERR_UNLOCK as SQLITE_IOERR_UNLOCK, + SQLITE_IOERR_VNODE as SQLITE_IOERR_VNODE, + SQLITE_IOERR_WRITE as SQLITE_IOERR_WRITE, + SQLITE_LIMIT_ATTACHED as SQLITE_LIMIT_ATTACHED, + SQLITE_LIMIT_COLUMN as SQLITE_LIMIT_COLUMN, + SQLITE_LIMIT_COMPOUND_SELECT as SQLITE_LIMIT_COMPOUND_SELECT, + SQLITE_LIMIT_EXPR_DEPTH as SQLITE_LIMIT_EXPR_DEPTH, + SQLITE_LIMIT_FUNCTION_ARG as SQLITE_LIMIT_FUNCTION_ARG, + SQLITE_LIMIT_LENGTH as SQLITE_LIMIT_LENGTH, + SQLITE_LIMIT_LIKE_PATTERN_LENGTH as SQLITE_LIMIT_LIKE_PATTERN_LENGTH, + SQLITE_LIMIT_SQL_LENGTH as SQLITE_LIMIT_SQL_LENGTH, + SQLITE_LIMIT_TRIGGER_DEPTH as SQLITE_LIMIT_TRIGGER_DEPTH, + SQLITE_LIMIT_VARIABLE_NUMBER as SQLITE_LIMIT_VARIABLE_NUMBER, + SQLITE_LIMIT_VDBE_OP as SQLITE_LIMIT_VDBE_OP, + SQLITE_LIMIT_WORKER_THREADS as SQLITE_LIMIT_WORKER_THREADS, + SQLITE_LOCKED as SQLITE_LOCKED, + SQLITE_LOCKED_SHAREDCACHE as SQLITE_LOCKED_SHAREDCACHE, + SQLITE_LOCKED_VTAB as SQLITE_LOCKED_VTAB, + SQLITE_MISMATCH as SQLITE_MISMATCH, + SQLITE_MISUSE as SQLITE_MISUSE, + SQLITE_NOLFS as SQLITE_NOLFS, + SQLITE_NOMEM as SQLITE_NOMEM, + SQLITE_NOTADB as SQLITE_NOTADB, + SQLITE_NOTFOUND as SQLITE_NOTFOUND, + SQLITE_NOTICE as SQLITE_NOTICE, + SQLITE_NOTICE_RECOVER_ROLLBACK as SQLITE_NOTICE_RECOVER_ROLLBACK, + SQLITE_NOTICE_RECOVER_WAL as SQLITE_NOTICE_RECOVER_WAL, + SQLITE_OK_LOAD_PERMANENTLY as SQLITE_OK_LOAD_PERMANENTLY, + SQLITE_OK_SYMLINK as SQLITE_OK_SYMLINK, + SQLITE_PERM as SQLITE_PERM, + SQLITE_PROTOCOL as SQLITE_PROTOCOL, + SQLITE_RANGE as SQLITE_RANGE, + SQLITE_READONLY as SQLITE_READONLY, + SQLITE_READONLY_CANTINIT as SQLITE_READONLY_CANTINIT, + SQLITE_READONLY_CANTLOCK as SQLITE_READONLY_CANTLOCK, + SQLITE_READONLY_DBMOVED as SQLITE_READONLY_DBMOVED, + SQLITE_READONLY_DIRECTORY as SQLITE_READONLY_DIRECTORY, + SQLITE_READONLY_RECOVERY as SQLITE_READONLY_RECOVERY, + SQLITE_READONLY_ROLLBACK as SQLITE_READONLY_ROLLBACK, + SQLITE_ROW as SQLITE_ROW, + SQLITE_SCHEMA as SQLITE_SCHEMA, + SQLITE_TOOBIG as SQLITE_TOOBIG, + SQLITE_WARNING as SQLITE_WARNING, + SQLITE_WARNING_AUTOINDEX as SQLITE_WARNING_AUTOINDEX, + ) + +if sys.version_info < (3, 12): + from sqlite3.dbapi2 import enable_shared_cache as enable_shared_cache, version as version + +if sys.version_info < (3, 10): + from sqlite3.dbapi2 import OptimizedUnicode as OptimizedUnicode + +_CursorT = TypeVar("_CursorT", bound=Cursor) +_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None +# Data that is passed through adapters can be of any type accepted by an adapter. +_AdaptedInputData: TypeAlias = _SqliteData | Any +# The Mapping must really be a dict, but making it invariant is too annoying. +_Parameters: TypeAlias = SupportsLenAndGetItem[_AdaptedInputData] | Mapping[str, _AdaptedInputData] + +class _AnyParamWindowAggregateClass(Protocol): + def step(self, *args: Any) -> object: ... + def inverse(self, *args: Any) -> object: ... + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +class _WindowAggregateClass(Protocol): + step: Callable[..., object] + inverse: Callable[..., object] + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +class _AggregateProtocol(Protocol): + def step(self, value: int, /) -> object: ... + def finalize(self) -> int: ... + +class _SingleParamWindowAggregateClass(Protocol): + def step(self, param: Any, /) -> object: ... + def inverse(self, param: Any, /) -> object: ... + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +# These classes are implemented in the C module _sqlite3. At runtime, they're imported +# from there into sqlite3.dbapi2 and from that module to here. However, they +# consider themselves to live in the sqlite3.* namespace, so we'll define them here. + +class Error(Exception): + if sys.version_info >= (3, 11): + sqlite_errorcode: int + sqlite_errorname: str + +class DatabaseError(Error): ... +class DataError(DatabaseError): ... +class IntegrityError(DatabaseError): ... +class InterfaceError(Error): ... +class InternalError(DatabaseError): ... +class NotSupportedError(DatabaseError): ... +class OperationalError(DatabaseError): ... +class ProgrammingError(DatabaseError): ... +class Warning(Exception): ... + +class Connection: + @property + def DataError(self) -> type[DataError]: ... + @property + def DatabaseError(self) -> type[DatabaseError]: ... + @property + def Error(self) -> type[Error]: ... + @property + def IntegrityError(self) -> type[IntegrityError]: ... + @property + def InterfaceError(self) -> type[InterfaceError]: ... + @property + def InternalError(self) -> type[InternalError]: ... + @property + def NotSupportedError(self) -> type[NotSupportedError]: ... + @property + def OperationalError(self) -> type[OperationalError]: ... + @property + def ProgrammingError(self) -> type[ProgrammingError]: ... + @property + def Warning(self) -> type[Warning]: ... + @property + def in_transaction(self) -> bool: ... + isolation_level: str | None # one of '', 'DEFERRED', 'IMMEDIATE' or 'EXCLUSIVE' + @property + def total_changes(self) -> int: ... + if sys.version_info >= (3, 12): + @property + def autocommit(self) -> int: ... + @autocommit.setter + def autocommit(self, val: int) -> None: ... + row_factory: Any + text_factory: Any + if sys.version_info >= (3, 12): + def __init__( + self, + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + autocommit: bool = ..., + ) -> None: ... + else: + def __init__( + self, + database: StrOrBytesPath, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + ) -> None: ... + + def close(self) -> None: ... + if sys.version_info >= (3, 11): + def blobopen(self, table: str, column: str, row: int, /, *, readonly: bool = False, name: str = "main") -> Blob: ... + + def commit(self) -> None: ... + def create_aggregate(self, name: str, n_arg: int, aggregate_class: Callable[[], _AggregateProtocol]) -> None: ... + if sys.version_info >= (3, 11): + # num_params determines how many params will be passed to the aggregate class. We provide an overload + # for the case where num_params = 1, which is expected to be the common case. + @overload + def create_window_function( + self, name: str, num_params: Literal[1], aggregate_class: Callable[[], _SingleParamWindowAggregateClass] | None, / + ) -> None: ... + # And for num_params = -1, which means the aggregate must accept any number of parameters. + @overload + def create_window_function( + self, name: str, num_params: Literal[-1], aggregate_class: Callable[[], _AnyParamWindowAggregateClass] | None, / + ) -> None: ... + @overload + def create_window_function( + self, name: str, num_params: int, aggregate_class: Callable[[], _WindowAggregateClass] | None, / + ) -> None: ... + + def create_collation(self, name: str, callback: Callable[[str, str], int | SupportsIndex] | None, /) -> None: ... + def create_function( + self, name: str, narg: int, func: Callable[..., _SqliteData] | None, *, deterministic: bool = False + ) -> None: ... + @overload + def cursor(self, factory: None = None) -> Cursor: ... + @overload + def cursor(self, factory: Callable[[Connection], _CursorT]) -> _CursorT: ... + def execute(self, sql: str, parameters: _Parameters = ..., /) -> Cursor: ... + def executemany(self, sql: str, parameters: Iterable[_Parameters], /) -> Cursor: ... + def executescript(self, sql_script: str, /) -> Cursor: ... + def interrupt(self) -> None: ... + if sys.version_info >= (3, 13): + def iterdump(self, *, filter: str | None = None) -> Generator[str, None, None]: ... + else: + def iterdump(self) -> Generator[str, None, None]: ... + + def rollback(self) -> None: ... + def set_authorizer( + self, authorizer_callback: Callable[[int, str | None, str | None, str | None, str | None], int] | None + ) -> None: ... + def set_progress_handler(self, progress_handler: Callable[[], int | None] | None, n: int) -> None: ... + def set_trace_callback(self, trace_callback: Callable[[str], object] | None) -> None: ... + # enable_load_extension and load_extension is not available on python distributions compiled + # without sqlite3 loadable extension support. see footnotes https://docs.python.org/3/library/sqlite3.html#f1 + def enable_load_extension(self, enable: bool, /) -> None: ... + if sys.version_info >= (3, 12): + def load_extension(self, name: str, /, *, entrypoint: str | None = None) -> None: ... + else: + def load_extension(self, name: str, /) -> None: ... + + def backup( + self, + target: Connection, + *, + pages: int = -1, + progress: Callable[[int, int, int], object] | None = None, + name: str = "main", + sleep: float = 0.25, + ) -> None: ... + if sys.version_info >= (3, 11): + def setlimit(self, category: int, limit: int, /) -> int: ... + def getlimit(self, category: int, /) -> int: ... + def serialize(self, *, name: str = "main") -> bytes: ... + def deserialize(self, data: ReadableBuffer, /, *, name: str = "main") -> None: ... + if sys.version_info >= (3, 12): + def getconfig(self, op: int, /) -> bool: ... + def setconfig(self, op: int, enable: bool = True, /) -> bool: ... + + def __call__(self, sql: str, /) -> _Statement: ... + def __enter__(self) -> Self: ... + def __exit__( + self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / + ) -> Literal[False]: ... + +class Cursor(Iterator[Any]): + arraysize: int + @property + def connection(self) -> Connection: ... + # May be None, but using `| MaybeNone` (`| Any`) instead to avoid slightly annoying false positives. + @property + def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | MaybeNone: ... + @property + def lastrowid(self) -> int | None: ... + row_factory: Callable[[Cursor, Row], object] | None + @property + def rowcount(self) -> int: ... + def __init__(self, cursor: Connection, /) -> None: ... + def close(self) -> None: ... + def execute(self, sql: str, parameters: _Parameters = (), /) -> Self: ... + def executemany(self, sql: str, seq_of_parameters: Iterable[_Parameters], /) -> Self: ... + def executescript(self, sql_script: str, /) -> Cursor: ... + def fetchall(self) -> list[Any]: ... + def fetchmany(self, size: int | None = 1) -> list[Any]: ... + # Returns either a row (as created by the row_factory) or None, but + # putting None in the return annotation causes annoying false positives. + def fetchone(self) -> Any: ... + def setinputsizes(self, sizes: Unused, /) -> None: ... # does nothing + def setoutputsize(self, size: Unused, column: Unused = None, /) -> None: ... # does nothing + def __iter__(self) -> Self: ... + def __next__(self) -> Any: ... + +@final +class PrepareProtocol: + def __init__(self, *args: object, **kwargs: object) -> None: ... + +class Row(Sequence[Any]): + def __init__(self, cursor: Cursor, data: tuple[Any, ...], /) -> None: ... + def keys(self) -> list[str]: ... + @overload + def __getitem__(self, key: int | str, /) -> Any: ... + @overload + def __getitem__(self, key: slice, /) -> tuple[Any, ...]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[Any]: ... + def __len__(self) -> int: ... + # These return NotImplemented for anything that is not a Row. + def __eq__(self, value: object, /) -> bool: ... + def __ge__(self, value: object, /) -> bool: ... + def __gt__(self, value: object, /) -> bool: ... + def __le__(self, value: object, /) -> bool: ... + def __lt__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + +# This class is not exposed. It calls itself sqlite3.Statement. +@final +@type_check_only +class _Statement: ... + +if sys.version_info >= (3, 11): + @final + class Blob: + def close(self) -> None: ... + def read(self, length: int = -1, /) -> bytes: ... + def write(self, data: ReadableBuffer, /) -> None: ... + def tell(self) -> int: ... + # whence must be one of os.SEEK_SET, os.SEEK_CUR, os.SEEK_END + def seek(self, offset: int, origin: int = 0, /) -> None: ... + def __len__(self) -> int: ... + def __enter__(self) -> Self: ... + def __exit__(self, type: object, val: object, tb: object, /) -> Literal[False]: ... + def __getitem__(self, key: SupportsIndex | slice, /) -> int: ... + def __setitem__(self, key: SupportsIndex | slice, value: int, /) -> None: ... diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 3cb4b93e88fe1..d3ea3ef0e8963 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -1,22 +1,226 @@ -import sqlite3 import sys -from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsLenAndGetItem, Unused -from collections.abc import Callable, Generator, Iterable, Iterator, Mapping +from _sqlite3 import ( + PARSE_COLNAMES as PARSE_COLNAMES, + PARSE_DECLTYPES as PARSE_DECLTYPES, + SQLITE_ALTER_TABLE as SQLITE_ALTER_TABLE, + SQLITE_ANALYZE as SQLITE_ANALYZE, + SQLITE_ATTACH as SQLITE_ATTACH, + SQLITE_CREATE_INDEX as SQLITE_CREATE_INDEX, + SQLITE_CREATE_TABLE as SQLITE_CREATE_TABLE, + SQLITE_CREATE_TEMP_INDEX as SQLITE_CREATE_TEMP_INDEX, + SQLITE_CREATE_TEMP_TABLE as SQLITE_CREATE_TEMP_TABLE, + SQLITE_CREATE_TEMP_TRIGGER as SQLITE_CREATE_TEMP_TRIGGER, + SQLITE_CREATE_TEMP_VIEW as SQLITE_CREATE_TEMP_VIEW, + SQLITE_CREATE_TRIGGER as SQLITE_CREATE_TRIGGER, + SQLITE_CREATE_VIEW as SQLITE_CREATE_VIEW, + SQLITE_CREATE_VTABLE as SQLITE_CREATE_VTABLE, + SQLITE_DELETE as SQLITE_DELETE, + SQLITE_DENY as SQLITE_DENY, + SQLITE_DETACH as SQLITE_DETACH, + SQLITE_DONE as SQLITE_DONE, + SQLITE_DROP_INDEX as SQLITE_DROP_INDEX, + SQLITE_DROP_TABLE as SQLITE_DROP_TABLE, + SQLITE_DROP_TEMP_INDEX as SQLITE_DROP_TEMP_INDEX, + SQLITE_DROP_TEMP_TABLE as SQLITE_DROP_TEMP_TABLE, + SQLITE_DROP_TEMP_TRIGGER as SQLITE_DROP_TEMP_TRIGGER, + SQLITE_DROP_TEMP_VIEW as SQLITE_DROP_TEMP_VIEW, + SQLITE_DROP_TRIGGER as SQLITE_DROP_TRIGGER, + SQLITE_DROP_VIEW as SQLITE_DROP_VIEW, + SQLITE_DROP_VTABLE as SQLITE_DROP_VTABLE, + SQLITE_FUNCTION as SQLITE_FUNCTION, + SQLITE_IGNORE as SQLITE_IGNORE, + SQLITE_INSERT as SQLITE_INSERT, + SQLITE_OK as SQLITE_OK, + SQLITE_PRAGMA as SQLITE_PRAGMA, + SQLITE_READ as SQLITE_READ, + SQLITE_RECURSIVE as SQLITE_RECURSIVE, + SQLITE_REINDEX as SQLITE_REINDEX, + SQLITE_SAVEPOINT as SQLITE_SAVEPOINT, + SQLITE_SELECT as SQLITE_SELECT, + SQLITE_TRANSACTION as SQLITE_TRANSACTION, + SQLITE_UPDATE as SQLITE_UPDATE, + adapt as adapt, + adapters as adapters, + complete_statement as complete_statement, + connect as connect, + converters as converters, + enable_callback_tracebacks as enable_callback_tracebacks, + register_adapter as register_adapter, + register_converter as register_converter, + sqlite_version as sqlite_version, +) from datetime import date, datetime, time -from types import TracebackType -from typing import Any, Literal, Protocol, SupportsIndex, TypeVar, final, overload -from typing_extensions import Self, TypeAlias +from sqlite3 import ( + Connection as Connection, + Cursor as Cursor, + DatabaseError as DatabaseError, + DataError as DataError, + Error as Error, + IntegrityError as IntegrityError, + InterfaceError as InterfaceError, + InternalError as InternalError, + NotSupportedError as NotSupportedError, + OperationalError as OperationalError, + PrepareProtocol as PrepareProtocol, + ProgrammingError as ProgrammingError, + Row as Row, + Warning as Warning, +) -_T = TypeVar("_T") -_ConnectionT = TypeVar("_ConnectionT", bound=Connection) -_CursorT = TypeVar("_CursorT", bound=Cursor) -_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None -# Data that is passed through adapters can be of any type accepted by an adapter. -_AdaptedInputData: TypeAlias = _SqliteData | Any -# The Mapping must really be a dict, but making it invariant is too annoying. -_Parameters: TypeAlias = SupportsLenAndGetItem[_AdaptedInputData] | Mapping[str, _AdaptedInputData] -_Adapter: TypeAlias = Callable[[_T], _SqliteData] -_Converter: TypeAlias = Callable[[bytes], Any] +if sys.version_info >= (3, 12): + from _sqlite3 import ( + LEGACY_TRANSACTION_CONTROL as LEGACY_TRANSACTION_CONTROL, + SQLITE_DBCONFIG_DEFENSIVE as SQLITE_DBCONFIG_DEFENSIVE, + SQLITE_DBCONFIG_DQS_DDL as SQLITE_DBCONFIG_DQS_DDL, + SQLITE_DBCONFIG_DQS_DML as SQLITE_DBCONFIG_DQS_DML, + SQLITE_DBCONFIG_ENABLE_FKEY as SQLITE_DBCONFIG_ENABLE_FKEY, + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER as SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, + SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION as SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, + SQLITE_DBCONFIG_ENABLE_QPSG as SQLITE_DBCONFIG_ENABLE_QPSG, + SQLITE_DBCONFIG_ENABLE_TRIGGER as SQLITE_DBCONFIG_ENABLE_TRIGGER, + SQLITE_DBCONFIG_ENABLE_VIEW as SQLITE_DBCONFIG_ENABLE_VIEW, + SQLITE_DBCONFIG_LEGACY_ALTER_TABLE as SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, + SQLITE_DBCONFIG_LEGACY_FILE_FORMAT as SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, + SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE as SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, + SQLITE_DBCONFIG_RESET_DATABASE as SQLITE_DBCONFIG_RESET_DATABASE, + SQLITE_DBCONFIG_TRIGGER_EQP as SQLITE_DBCONFIG_TRIGGER_EQP, + SQLITE_DBCONFIG_TRUSTED_SCHEMA as SQLITE_DBCONFIG_TRUSTED_SCHEMA, + SQLITE_DBCONFIG_WRITABLE_SCHEMA as SQLITE_DBCONFIG_WRITABLE_SCHEMA, + ) + +if sys.version_info >= (3, 11): + from _sqlite3 import ( + SQLITE_ABORT as SQLITE_ABORT, + SQLITE_ABORT_ROLLBACK as SQLITE_ABORT_ROLLBACK, + SQLITE_AUTH as SQLITE_AUTH, + SQLITE_AUTH_USER as SQLITE_AUTH_USER, + SQLITE_BUSY as SQLITE_BUSY, + SQLITE_BUSY_RECOVERY as SQLITE_BUSY_RECOVERY, + SQLITE_BUSY_SNAPSHOT as SQLITE_BUSY_SNAPSHOT, + SQLITE_BUSY_TIMEOUT as SQLITE_BUSY_TIMEOUT, + SQLITE_CANTOPEN as SQLITE_CANTOPEN, + SQLITE_CANTOPEN_CONVPATH as SQLITE_CANTOPEN_CONVPATH, + SQLITE_CANTOPEN_DIRTYWAL as SQLITE_CANTOPEN_DIRTYWAL, + SQLITE_CANTOPEN_FULLPATH as SQLITE_CANTOPEN_FULLPATH, + SQLITE_CANTOPEN_ISDIR as SQLITE_CANTOPEN_ISDIR, + SQLITE_CANTOPEN_NOTEMPDIR as SQLITE_CANTOPEN_NOTEMPDIR, + SQLITE_CANTOPEN_SYMLINK as SQLITE_CANTOPEN_SYMLINK, + SQLITE_CONSTRAINT as SQLITE_CONSTRAINT, + SQLITE_CONSTRAINT_CHECK as SQLITE_CONSTRAINT_CHECK, + SQLITE_CONSTRAINT_COMMITHOOK as SQLITE_CONSTRAINT_COMMITHOOK, + SQLITE_CONSTRAINT_FOREIGNKEY as SQLITE_CONSTRAINT_FOREIGNKEY, + SQLITE_CONSTRAINT_FUNCTION as SQLITE_CONSTRAINT_FUNCTION, + SQLITE_CONSTRAINT_NOTNULL as SQLITE_CONSTRAINT_NOTNULL, + SQLITE_CONSTRAINT_PINNED as SQLITE_CONSTRAINT_PINNED, + SQLITE_CONSTRAINT_PRIMARYKEY as SQLITE_CONSTRAINT_PRIMARYKEY, + SQLITE_CONSTRAINT_ROWID as SQLITE_CONSTRAINT_ROWID, + SQLITE_CONSTRAINT_TRIGGER as SQLITE_CONSTRAINT_TRIGGER, + SQLITE_CONSTRAINT_UNIQUE as SQLITE_CONSTRAINT_UNIQUE, + SQLITE_CONSTRAINT_VTAB as SQLITE_CONSTRAINT_VTAB, + SQLITE_CORRUPT as SQLITE_CORRUPT, + SQLITE_CORRUPT_INDEX as SQLITE_CORRUPT_INDEX, + SQLITE_CORRUPT_SEQUENCE as SQLITE_CORRUPT_SEQUENCE, + SQLITE_CORRUPT_VTAB as SQLITE_CORRUPT_VTAB, + SQLITE_EMPTY as SQLITE_EMPTY, + SQLITE_ERROR as SQLITE_ERROR, + SQLITE_ERROR_MISSING_COLLSEQ as SQLITE_ERROR_MISSING_COLLSEQ, + SQLITE_ERROR_RETRY as SQLITE_ERROR_RETRY, + SQLITE_ERROR_SNAPSHOT as SQLITE_ERROR_SNAPSHOT, + SQLITE_FORMAT as SQLITE_FORMAT, + SQLITE_FULL as SQLITE_FULL, + SQLITE_INTERNAL as SQLITE_INTERNAL, + SQLITE_INTERRUPT as SQLITE_INTERRUPT, + SQLITE_IOERR as SQLITE_IOERR, + SQLITE_IOERR_ACCESS as SQLITE_IOERR_ACCESS, + SQLITE_IOERR_AUTH as SQLITE_IOERR_AUTH, + SQLITE_IOERR_BEGIN_ATOMIC as SQLITE_IOERR_BEGIN_ATOMIC, + SQLITE_IOERR_BLOCKED as SQLITE_IOERR_BLOCKED, + SQLITE_IOERR_CHECKRESERVEDLOCK as SQLITE_IOERR_CHECKRESERVEDLOCK, + SQLITE_IOERR_CLOSE as SQLITE_IOERR_CLOSE, + SQLITE_IOERR_COMMIT_ATOMIC as SQLITE_IOERR_COMMIT_ATOMIC, + SQLITE_IOERR_CONVPATH as SQLITE_IOERR_CONVPATH, + SQLITE_IOERR_CORRUPTFS as SQLITE_IOERR_CORRUPTFS, + SQLITE_IOERR_DATA as SQLITE_IOERR_DATA, + SQLITE_IOERR_DELETE as SQLITE_IOERR_DELETE, + SQLITE_IOERR_DELETE_NOENT as SQLITE_IOERR_DELETE_NOENT, + SQLITE_IOERR_DIR_CLOSE as SQLITE_IOERR_DIR_CLOSE, + SQLITE_IOERR_DIR_FSYNC as SQLITE_IOERR_DIR_FSYNC, + SQLITE_IOERR_FSTAT as SQLITE_IOERR_FSTAT, + SQLITE_IOERR_FSYNC as SQLITE_IOERR_FSYNC, + SQLITE_IOERR_GETTEMPPATH as SQLITE_IOERR_GETTEMPPATH, + SQLITE_IOERR_LOCK as SQLITE_IOERR_LOCK, + SQLITE_IOERR_MMAP as SQLITE_IOERR_MMAP, + SQLITE_IOERR_NOMEM as SQLITE_IOERR_NOMEM, + SQLITE_IOERR_RDLOCK as SQLITE_IOERR_RDLOCK, + SQLITE_IOERR_READ as SQLITE_IOERR_READ, + SQLITE_IOERR_ROLLBACK_ATOMIC as SQLITE_IOERR_ROLLBACK_ATOMIC, + SQLITE_IOERR_SEEK as SQLITE_IOERR_SEEK, + SQLITE_IOERR_SHMLOCK as SQLITE_IOERR_SHMLOCK, + SQLITE_IOERR_SHMMAP as SQLITE_IOERR_SHMMAP, + SQLITE_IOERR_SHMOPEN as SQLITE_IOERR_SHMOPEN, + SQLITE_IOERR_SHMSIZE as SQLITE_IOERR_SHMSIZE, + SQLITE_IOERR_SHORT_READ as SQLITE_IOERR_SHORT_READ, + SQLITE_IOERR_TRUNCATE as SQLITE_IOERR_TRUNCATE, + SQLITE_IOERR_UNLOCK as SQLITE_IOERR_UNLOCK, + SQLITE_IOERR_VNODE as SQLITE_IOERR_VNODE, + SQLITE_IOERR_WRITE as SQLITE_IOERR_WRITE, + SQLITE_LIMIT_ATTACHED as SQLITE_LIMIT_ATTACHED, + SQLITE_LIMIT_COLUMN as SQLITE_LIMIT_COLUMN, + SQLITE_LIMIT_COMPOUND_SELECT as SQLITE_LIMIT_COMPOUND_SELECT, + SQLITE_LIMIT_EXPR_DEPTH as SQLITE_LIMIT_EXPR_DEPTH, + SQLITE_LIMIT_FUNCTION_ARG as SQLITE_LIMIT_FUNCTION_ARG, + SQLITE_LIMIT_LENGTH as SQLITE_LIMIT_LENGTH, + SQLITE_LIMIT_LIKE_PATTERN_LENGTH as SQLITE_LIMIT_LIKE_PATTERN_LENGTH, + SQLITE_LIMIT_SQL_LENGTH as SQLITE_LIMIT_SQL_LENGTH, + SQLITE_LIMIT_TRIGGER_DEPTH as SQLITE_LIMIT_TRIGGER_DEPTH, + SQLITE_LIMIT_VARIABLE_NUMBER as SQLITE_LIMIT_VARIABLE_NUMBER, + SQLITE_LIMIT_VDBE_OP as SQLITE_LIMIT_VDBE_OP, + SQLITE_LIMIT_WORKER_THREADS as SQLITE_LIMIT_WORKER_THREADS, + SQLITE_LOCKED as SQLITE_LOCKED, + SQLITE_LOCKED_SHAREDCACHE as SQLITE_LOCKED_SHAREDCACHE, + SQLITE_LOCKED_VTAB as SQLITE_LOCKED_VTAB, + SQLITE_MISMATCH as SQLITE_MISMATCH, + SQLITE_MISUSE as SQLITE_MISUSE, + SQLITE_NOLFS as SQLITE_NOLFS, + SQLITE_NOMEM as SQLITE_NOMEM, + SQLITE_NOTADB as SQLITE_NOTADB, + SQLITE_NOTFOUND as SQLITE_NOTFOUND, + SQLITE_NOTICE as SQLITE_NOTICE, + SQLITE_NOTICE_RECOVER_ROLLBACK as SQLITE_NOTICE_RECOVER_ROLLBACK, + SQLITE_NOTICE_RECOVER_WAL as SQLITE_NOTICE_RECOVER_WAL, + SQLITE_OK_LOAD_PERMANENTLY as SQLITE_OK_LOAD_PERMANENTLY, + SQLITE_OK_SYMLINK as SQLITE_OK_SYMLINK, + SQLITE_PERM as SQLITE_PERM, + SQLITE_PROTOCOL as SQLITE_PROTOCOL, + SQLITE_RANGE as SQLITE_RANGE, + SQLITE_READONLY as SQLITE_READONLY, + SQLITE_READONLY_CANTINIT as SQLITE_READONLY_CANTINIT, + SQLITE_READONLY_CANTLOCK as SQLITE_READONLY_CANTLOCK, + SQLITE_READONLY_DBMOVED as SQLITE_READONLY_DBMOVED, + SQLITE_READONLY_DIRECTORY as SQLITE_READONLY_DIRECTORY, + SQLITE_READONLY_RECOVERY as SQLITE_READONLY_RECOVERY, + SQLITE_READONLY_ROLLBACK as SQLITE_READONLY_ROLLBACK, + SQLITE_ROW as SQLITE_ROW, + SQLITE_SCHEMA as SQLITE_SCHEMA, + SQLITE_TOOBIG as SQLITE_TOOBIG, + SQLITE_WARNING as SQLITE_WARNING, + SQLITE_WARNING_AUTOINDEX as SQLITE_WARNING_AUTOINDEX, + ) + from sqlite3 import Blob as Blob + +if sys.version_info < (3, 14): + # Deprecated and removed from _sqlite3 in 3.12, but removed from here in 3.14. + version: str + +if sys.version_info < (3, 12): + if sys.version_info >= (3, 10): + # deprecation wrapper that has a different name for the argument... + def enable_shared_cache(enable: int) -> None: ... + else: + from _sqlite3 import enable_shared_cache as enable_shared_cache + +if sys.version_info < (3, 10): + from _sqlite3 import OptimizedUnicode as OptimizedUnicode paramstyle: str threadsafety: int @@ -29,527 +233,9 @@ def DateFromTicks(ticks: float) -> Date: ... def TimeFromTicks(ticks: float) -> Time: ... def TimestampFromTicks(ticks: float) -> Timestamp: ... -version_info: tuple[int, int, int] +if sys.version_info < (3, 14): + # Deprecated in 3.12, removed in 3.14. + version_info: tuple[int, int, int] + sqlite_version_info: tuple[int, int, int] Binary = memoryview - -# The remaining definitions are imported from _sqlite3. - -PARSE_COLNAMES: int -PARSE_DECLTYPES: int -SQLITE_ALTER_TABLE: int -SQLITE_ANALYZE: int -SQLITE_ATTACH: int -SQLITE_CREATE_INDEX: int -SQLITE_CREATE_TABLE: int -SQLITE_CREATE_TEMP_INDEX: int -SQLITE_CREATE_TEMP_TABLE: int -SQLITE_CREATE_TEMP_TRIGGER: int -SQLITE_CREATE_TEMP_VIEW: int -SQLITE_CREATE_TRIGGER: int -SQLITE_CREATE_VIEW: int -SQLITE_CREATE_VTABLE: int -SQLITE_DELETE: int -SQLITE_DENY: int -SQLITE_DETACH: int -SQLITE_DONE: int -SQLITE_DROP_INDEX: int -SQLITE_DROP_TABLE: int -SQLITE_DROP_TEMP_INDEX: int -SQLITE_DROP_TEMP_TABLE: int -SQLITE_DROP_TEMP_TRIGGER: int -SQLITE_DROP_TEMP_VIEW: int -SQLITE_DROP_TRIGGER: int -SQLITE_DROP_VIEW: int -SQLITE_DROP_VTABLE: int -SQLITE_FUNCTION: int -SQLITE_IGNORE: int -SQLITE_INSERT: int -SQLITE_OK: int -if sys.version_info >= (3, 11): - SQLITE_LIMIT_LENGTH: int - SQLITE_LIMIT_SQL_LENGTH: int - SQLITE_LIMIT_COLUMN: int - SQLITE_LIMIT_EXPR_DEPTH: int - SQLITE_LIMIT_COMPOUND_SELECT: int - SQLITE_LIMIT_VDBE_OP: int - SQLITE_LIMIT_FUNCTION_ARG: int - SQLITE_LIMIT_ATTACHED: int - SQLITE_LIMIT_LIKE_PATTERN_LENGTH: int - SQLITE_LIMIT_VARIABLE_NUMBER: int - SQLITE_LIMIT_TRIGGER_DEPTH: int - SQLITE_LIMIT_WORKER_THREADS: int -SQLITE_PRAGMA: int -SQLITE_READ: int -SQLITE_REINDEX: int -SQLITE_RECURSIVE: int -SQLITE_SAVEPOINT: int -SQLITE_SELECT: int -SQLITE_TRANSACTION: int -SQLITE_UPDATE: int -adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]] -converters: dict[str, _Converter] -sqlite_version: str -version: str - -if sys.version_info >= (3, 11): - SQLITE_ABORT: int - SQLITE_ABORT_ROLLBACK: int - SQLITE_AUTH: int - SQLITE_AUTH_USER: int - SQLITE_BUSY: int - SQLITE_BUSY_RECOVERY: int - SQLITE_BUSY_SNAPSHOT: int - SQLITE_BUSY_TIMEOUT: int - SQLITE_CANTOPEN: int - SQLITE_CANTOPEN_CONVPATH: int - SQLITE_CANTOPEN_DIRTYWAL: int - SQLITE_CANTOPEN_FULLPATH: int - SQLITE_CANTOPEN_ISDIR: int - SQLITE_CANTOPEN_NOTEMPDIR: int - SQLITE_CANTOPEN_SYMLINK: int - SQLITE_CONSTRAINT: int - SQLITE_CONSTRAINT_CHECK: int - SQLITE_CONSTRAINT_COMMITHOOK: int - SQLITE_CONSTRAINT_FOREIGNKEY: int - SQLITE_CONSTRAINT_FUNCTION: int - SQLITE_CONSTRAINT_NOTNULL: int - SQLITE_CONSTRAINT_PINNED: int - SQLITE_CONSTRAINT_PRIMARYKEY: int - SQLITE_CONSTRAINT_ROWID: int - SQLITE_CONSTRAINT_TRIGGER: int - SQLITE_CONSTRAINT_UNIQUE: int - SQLITE_CONSTRAINT_VTAB: int - SQLITE_CORRUPT: int - SQLITE_CORRUPT_INDEX: int - SQLITE_CORRUPT_SEQUENCE: int - SQLITE_CORRUPT_VTAB: int - SQLITE_EMPTY: int - SQLITE_ERROR: int - SQLITE_ERROR_MISSING_COLLSEQ: int - SQLITE_ERROR_RETRY: int - SQLITE_ERROR_SNAPSHOT: int - SQLITE_FORMAT: int - SQLITE_FULL: int - SQLITE_INTERNAL: int - SQLITE_INTERRUPT: int - SQLITE_IOERR: int - SQLITE_IOERR_ACCESS: int - SQLITE_IOERR_AUTH: int - SQLITE_IOERR_BEGIN_ATOMIC: int - SQLITE_IOERR_BLOCKED: int - SQLITE_IOERR_CHECKRESERVEDLOCK: int - SQLITE_IOERR_CLOSE: int - SQLITE_IOERR_COMMIT_ATOMIC: int - SQLITE_IOERR_CONVPATH: int - SQLITE_IOERR_CORRUPTFS: int - SQLITE_IOERR_DATA: int - SQLITE_IOERR_DELETE: int - SQLITE_IOERR_DELETE_NOENT: int - SQLITE_IOERR_DIR_CLOSE: int - SQLITE_IOERR_DIR_FSYNC: int - SQLITE_IOERR_FSTAT: int - SQLITE_IOERR_FSYNC: int - SQLITE_IOERR_GETTEMPPATH: int - SQLITE_IOERR_LOCK: int - SQLITE_IOERR_MMAP: int - SQLITE_IOERR_NOMEM: int - SQLITE_IOERR_RDLOCK: int - SQLITE_IOERR_READ: int - SQLITE_IOERR_ROLLBACK_ATOMIC: int - SQLITE_IOERR_SEEK: int - SQLITE_IOERR_SHMLOCK: int - SQLITE_IOERR_SHMMAP: int - SQLITE_IOERR_SHMOPEN: int - SQLITE_IOERR_SHMSIZE: int - SQLITE_IOERR_SHORT_READ: int - SQLITE_IOERR_TRUNCATE: int - SQLITE_IOERR_UNLOCK: int - SQLITE_IOERR_VNODE: int - SQLITE_IOERR_WRITE: int - SQLITE_LOCKED: int - SQLITE_LOCKED_SHAREDCACHE: int - SQLITE_LOCKED_VTAB: int - SQLITE_MISMATCH: int - SQLITE_MISUSE: int - SQLITE_NOLFS: int - SQLITE_NOMEM: int - SQLITE_NOTADB: int - SQLITE_NOTFOUND: int - SQLITE_NOTICE: int - SQLITE_NOTICE_RECOVER_ROLLBACK: int - SQLITE_NOTICE_RECOVER_WAL: int - SQLITE_OK_LOAD_PERMANENTLY: int - SQLITE_OK_SYMLINK: int - SQLITE_PERM: int - SQLITE_PROTOCOL: int - SQLITE_RANGE: int - SQLITE_READONLY: int - SQLITE_READONLY_CANTINIT: int - SQLITE_READONLY_CANTLOCK: int - SQLITE_READONLY_DBMOVED: int - SQLITE_READONLY_DIRECTORY: int - SQLITE_READONLY_RECOVERY: int - SQLITE_READONLY_ROLLBACK: int - SQLITE_ROW: int - SQLITE_SCHEMA: int - SQLITE_TOOBIG: int - SQLITE_WARNING: int - SQLITE_WARNING_AUTOINDEX: int - -if sys.version_info >= (3, 12): - LEGACY_TRANSACTION_CONTROL: int - SQLITE_DBCONFIG_DEFENSIVE: int - SQLITE_DBCONFIG_DQS_DDL: int - SQLITE_DBCONFIG_DQS_DML: int - SQLITE_DBCONFIG_ENABLE_FKEY: int - SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: int - SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: int - SQLITE_DBCONFIG_ENABLE_QPSG: int - SQLITE_DBCONFIG_ENABLE_TRIGGER: int - SQLITE_DBCONFIG_ENABLE_VIEW: int - SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: int - SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: int - SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: int - SQLITE_DBCONFIG_RESET_DATABASE: int - SQLITE_DBCONFIG_TRIGGER_EQP: int - SQLITE_DBCONFIG_TRUSTED_SCHEMA: int - SQLITE_DBCONFIG_WRITABLE_SCHEMA: int - -# Can take or return anything depending on what's in the registry. -@overload -def adapt(obj: Any, proto: Any, /) -> Any: ... -@overload -def adapt(obj: Any, proto: Any, alt: _T, /) -> Any | _T: ... -def complete_statement(statement: str) -> bool: ... - -if sys.version_info >= (3, 12): - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - cached_statements: int = 128, - uri: bool = False, - *, - autocommit: bool = ..., - ) -> Connection: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float, - detect_types: int, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, - check_same_thread: bool, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - *, - autocommit: bool = ..., - ) -> _ConnectionT: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - *, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - autocommit: bool = ..., - ) -> _ConnectionT: ... - -else: - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - cached_statements: int = 128, - uri: bool = False, - ) -> Connection: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float, - detect_types: int, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None, - check_same_thread: bool, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - ) -> _ConnectionT: ... - @overload - def connect( - database: StrOrBytesPath, - timeout: float = 5.0, - detect_types: int = 0, - isolation_level: Literal["DEFERRED", "EXCLUSIVE", "IMMEDIATE"] | None = "DEFERRED", - check_same_thread: bool = True, - *, - factory: type[_ConnectionT], - cached_statements: int = 128, - uri: bool = False, - ) -> _ConnectionT: ... - -def enable_callback_tracebacks(enable: bool, /) -> None: ... - -if sys.version_info < (3, 12): - # takes a pos-or-keyword argument because there is a C wrapper - def enable_shared_cache(enable: int) -> None: ... - -if sys.version_info >= (3, 10): - def register_adapter(type: type[_T], adapter: _Adapter[_T], /) -> None: ... - def register_converter(typename: str, converter: _Converter, /) -> None: ... - -else: - def register_adapter(type: type[_T], caster: _Adapter[_T], /) -> None: ... - def register_converter(name: str, converter: _Converter, /) -> None: ... - -class _AggregateProtocol(Protocol): - def step(self, value: int, /) -> object: ... - def finalize(self) -> int: ... - -class _SingleParamWindowAggregateClass(Protocol): - def step(self, param: Any, /) -> object: ... - def inverse(self, param: Any, /) -> object: ... - def value(self) -> _SqliteData: ... - def finalize(self) -> _SqliteData: ... - -class _AnyParamWindowAggregateClass(Protocol): - def step(self, *args: Any) -> object: ... - def inverse(self, *args: Any) -> object: ... - def value(self) -> _SqliteData: ... - def finalize(self) -> _SqliteData: ... - -class _WindowAggregateClass(Protocol): - step: Callable[..., object] - inverse: Callable[..., object] - def value(self) -> _SqliteData: ... - def finalize(self) -> _SqliteData: ... - -class Connection: - @property - def DataError(self) -> type[sqlite3.DataError]: ... - @property - def DatabaseError(self) -> type[sqlite3.DatabaseError]: ... - @property - def Error(self) -> type[sqlite3.Error]: ... - @property - def IntegrityError(self) -> type[sqlite3.IntegrityError]: ... - @property - def InterfaceError(self) -> type[sqlite3.InterfaceError]: ... - @property - def InternalError(self) -> type[sqlite3.InternalError]: ... - @property - def NotSupportedError(self) -> type[sqlite3.NotSupportedError]: ... - @property - def OperationalError(self) -> type[sqlite3.OperationalError]: ... - @property - def ProgrammingError(self) -> type[sqlite3.ProgrammingError]: ... - @property - def Warning(self) -> type[sqlite3.Warning]: ... - @property - def in_transaction(self) -> bool: ... - isolation_level: str | None # one of '', 'DEFERRED', 'IMMEDIATE' or 'EXCLUSIVE' - @property - def total_changes(self) -> int: ... - if sys.version_info >= (3, 12): - @property - def autocommit(self) -> int: ... - @autocommit.setter - def autocommit(self, val: int) -> None: ... - row_factory: Any - text_factory: Any - if sys.version_info >= (3, 12): - def __init__( - self, - database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., - autocommit: bool = ..., - ) -> None: ... - else: - def __init__( - self, - database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., - ) -> None: ... - - def close(self) -> None: ... - if sys.version_info >= (3, 11): - def blobopen(self, table: str, column: str, row: int, /, *, readonly: bool = False, name: str = "main") -> Blob: ... - - def commit(self) -> None: ... - def create_aggregate(self, name: str, n_arg: int, aggregate_class: Callable[[], _AggregateProtocol]) -> None: ... - if sys.version_info >= (3, 11): - # num_params determines how many params will be passed to the aggregate class. We provide an overload - # for the case where num_params = 1, which is expected to be the common case. - @overload - def create_window_function( - self, name: str, num_params: Literal[1], aggregate_class: Callable[[], _SingleParamWindowAggregateClass] | None, / - ) -> None: ... - # And for num_params = -1, which means the aggregate must accept any number of parameters. - @overload - def create_window_function( - self, name: str, num_params: Literal[-1], aggregate_class: Callable[[], _AnyParamWindowAggregateClass] | None, / - ) -> None: ... - @overload - def create_window_function( - self, name: str, num_params: int, aggregate_class: Callable[[], _WindowAggregateClass] | None, / - ) -> None: ... - - def create_collation(self, name: str, callback: Callable[[str, str], int | SupportsIndex] | None, /) -> None: ... - def create_function( - self, name: str, narg: int, func: Callable[..., _SqliteData] | None, *, deterministic: bool = False - ) -> None: ... - @overload - def cursor(self, factory: None = None) -> Cursor: ... - @overload - def cursor(self, factory: Callable[[Connection], _CursorT]) -> _CursorT: ... - def execute(self, sql: str, parameters: _Parameters = ..., /) -> Cursor: ... - def executemany(self, sql: str, parameters: Iterable[_Parameters], /) -> Cursor: ... - def executescript(self, sql_script: str, /) -> Cursor: ... - def interrupt(self) -> None: ... - if sys.version_info >= (3, 13): - def iterdump(self, *, filter: str | None = None) -> Generator[str, None, None]: ... - else: - def iterdump(self) -> Generator[str, None, None]: ... - - def rollback(self) -> None: ... - def set_authorizer( - self, authorizer_callback: Callable[[int, str | None, str | None, str | None, str | None], int] | None - ) -> None: ... - def set_progress_handler(self, progress_handler: Callable[[], int | None] | None, n: int) -> None: ... - def set_trace_callback(self, trace_callback: Callable[[str], object] | None) -> None: ... - # enable_load_extension and load_extension is not available on python distributions compiled - # without sqlite3 loadable extension support. see footnotes https://docs.python.org/3/library/sqlite3.html#f1 - def enable_load_extension(self, enable: bool, /) -> None: ... - def load_extension(self, name: str, /) -> None: ... - def backup( - self, - target: Connection, - *, - pages: int = -1, - progress: Callable[[int, int, int], object] | None = None, - name: str = "main", - sleep: float = 0.25, - ) -> None: ... - if sys.version_info >= (3, 11): - def setlimit(self, category: int, limit: int, /) -> int: ... - def getlimit(self, category: int, /) -> int: ... - def serialize(self, *, name: str = "main") -> bytes: ... - def deserialize(self, data: ReadableBuffer, /, *, name: str = "main") -> None: ... - if sys.version_info >= (3, 12): - def getconfig(self, op: int, /) -> bool: ... - def setconfig(self, op: int, enable: bool = True, /) -> bool: ... - - def __call__(self, sql: str, /) -> _Statement: ... - def __enter__(self) -> Self: ... - def __exit__( - self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None, / - ) -> Literal[False]: ... - -class Cursor(Iterator[Any]): - arraysize: int - @property - def connection(self) -> Connection: ... - # May be None, but using | Any instead to avoid slightly annoying false positives. - @property - def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | Any: ... - @property - def lastrowid(self) -> int | None: ... - row_factory: Callable[[Cursor, Row], object] | None - @property - def rowcount(self) -> int: ... - def __init__(self, cursor: Connection, /) -> None: ... - def close(self) -> None: ... - def execute(self, sql: str, parameters: _Parameters = (), /) -> Self: ... - def executemany(self, sql: str, seq_of_parameters: Iterable[_Parameters], /) -> Self: ... - def executescript(self, sql_script: str, /) -> Cursor: ... - def fetchall(self) -> list[Any]: ... - def fetchmany(self, size: int | None = 1) -> list[Any]: ... - # Returns either a row (as created by the row_factory) or None, but - # putting None in the return annotation causes annoying false positives. - def fetchone(self) -> Any: ... - def setinputsizes(self, sizes: Unused, /) -> None: ... # does nothing - def setoutputsize(self, size: Unused, column: Unused = None, /) -> None: ... # does nothing - def __iter__(self) -> Self: ... - def __next__(self) -> Any: ... - -class Error(Exception): - if sys.version_info >= (3, 11): - sqlite_errorcode: int - sqlite_errorname: str - -class DatabaseError(Error): ... -class DataError(DatabaseError): ... -class IntegrityError(DatabaseError): ... -class InterfaceError(Error): ... -class InternalError(DatabaseError): ... -class NotSupportedError(DatabaseError): ... -class OperationalError(DatabaseError): ... - -if sys.version_info < (3, 10): - OptimizedUnicode = str - -@final -class PrepareProtocol: - def __init__(self, *args: object, **kwargs: object) -> None: ... - -class ProgrammingError(DatabaseError): ... - -class Row: - def __init__(self, cursor: Cursor, data: tuple[Any, ...], /) -> None: ... - def keys(self) -> list[str]: ... - @overload - def __getitem__(self, key: int | str, /) -> Any: ... - @overload - def __getitem__(self, key: slice, /) -> tuple[Any, ...]: ... - def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[Any]: ... - def __len__(self) -> int: ... - # These return NotImplemented for anything that is not a Row. - def __eq__(self, value: object, /) -> bool: ... - def __ge__(self, value: object, /) -> bool: ... - def __gt__(self, value: object, /) -> bool: ... - def __le__(self, value: object, /) -> bool: ... - def __lt__(self, value: object, /) -> bool: ... - def __ne__(self, value: object, /) -> bool: ... - -@final -class _Statement: ... - -class Warning(Exception): ... - -if sys.version_info >= (3, 11): - @final - class Blob: - def close(self) -> None: ... - def read(self, length: int = -1, /) -> bytes: ... - def write(self, data: ReadableBuffer, /) -> None: ... - def tell(self) -> int: ... - # whence must be one of os.SEEK_SET, os.SEEK_CUR, os.SEEK_END - def seek(self, offset: int, origin: int = 0, /) -> None: ... - def __len__(self) -> int: ... - def __enter__(self) -> Self: ... - def __exit__(self, type: object, val: object, tb: object, /) -> Literal[False]: ... - def __getitem__(self, key: SupportsIndex | slice, /) -> int: ... - def __setitem__(self, key: SupportsIndex | slice, value: int, /) -> None: ... diff --git a/mypy/typeshed/stdlib/sqlite3/dump.pyi b/mypy/typeshed/stdlib/sqlite3/dump.pyi new file mode 100644 index 0000000000000..ed95fa46e1c78 --- /dev/null +++ b/mypy/typeshed/stdlib/sqlite3/dump.pyi @@ -0,0 +1,2 @@ +# This file is intentionally empty. The runtime module contains only +# private functions. diff --git a/mypy/typeshed/stdlib/sre_constants.pyi b/mypy/typeshed/stdlib/sre_constants.pyi index d522372c438ce..383f0f7eb8bd6 100644 --- a/mypy/typeshed/stdlib/sre_constants.pyi +++ b/mypy/typeshed/stdlib/sre_constants.pyi @@ -1,4 +1,5 @@ import sys +from re import error as error from typing import Any from typing_extensions import Self @@ -6,14 +7,6 @@ MAXGROUPS: int MAGIC: int -class error(Exception): - msg: str - pattern: str | bytes | None - pos: int | None - lineno: int - colno: int - def __init__(self, msg: str, pattern: str | bytes | None = None, pos: int | None = None) -> None: ... - class _NamedIntConstant(int): name: Any def __new__(cls, value: int, name: str) -> Self: ... @@ -30,7 +23,8 @@ AT_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] AT_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] CH_LOCALE: dict[_NamedIntConstant, _NamedIntConstant] CH_UNICODE: dict[_NamedIntConstant, _NamedIntConstant] -SRE_FLAG_TEMPLATE: int +if sys.version_info < (3, 13): + SRE_FLAG_TEMPLATE: int SRE_FLAG_IGNORECASE: int SRE_FLAG_LOCALE: int SRE_FLAG_MULTILINE: int diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index 81c68c69ec4e9..f587b51d5ac02 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -1,18 +1,51 @@ import enum import socket import sys +from _ssl import ( + _DEFAULT_CIPHERS as _DEFAULT_CIPHERS, + _OPENSSL_API_VERSION as _OPENSSL_API_VERSION, + HAS_ALPN as HAS_ALPN, + HAS_ECDH as HAS_ECDH, + HAS_NPN as HAS_NPN, + HAS_SNI as HAS_SNI, + OPENSSL_VERSION as OPENSSL_VERSION, + OPENSSL_VERSION_INFO as OPENSSL_VERSION_INFO, + OPENSSL_VERSION_NUMBER as OPENSSL_VERSION_NUMBER, + HAS_SSLv2 as HAS_SSLv2, + HAS_SSLv3 as HAS_SSLv3, + HAS_TLSv1 as HAS_TLSv1, + HAS_TLSv1_1 as HAS_TLSv1_1, + HAS_TLSv1_2 as HAS_TLSv1_2, + HAS_TLSv1_3 as HAS_TLSv1_3, + MemoryBIO as MemoryBIO, + RAND_add as RAND_add, + RAND_bytes as RAND_bytes, + RAND_status as RAND_status, + SSLSession as SSLSession, + _PasswordType as _PasswordType, # typeshed only, but re-export for other type stubs to use + _SSLContext, +) from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer from collections.abc import Callable, Iterable -from typing import Any, Literal, NamedTuple, TypedDict, final, overload +from typing import Any, Literal, NamedTuple, TypedDict, overload, type_check_only from typing_extensions import Never, Self, TypeAlias +if sys.version_info >= (3, 13): + from _ssl import HAS_PSK as HAS_PSK + +if sys.version_info < (3, 12): + from _ssl import RAND_pseudo_bytes as RAND_pseudo_bytes + +if sys.version_info < (3, 10): + from _ssl import RAND_egd as RAND_egd + +if sys.platform == "win32": + from _ssl import enum_certificates as enum_certificates, enum_crls as enum_crls + _PCTRTT: TypeAlias = tuple[tuple[str, str], ...] _PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] _PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT] _PeerCertRetType: TypeAlias = _PeerCertRetDictType | bytes | None -_EnumRetType: TypeAlias = list[tuple[bytes, str, set[str] | bool]] -_PasswordType: TypeAlias = Callable[[], str | bytes | bytearray] | str | bytes | bytearray - _SrvnmeCbType: TypeAlias = Callable[[SSLSocket | SSLObject, str | None, SSLSocket], int | None] socket_error = OSError @@ -98,15 +131,6 @@ else: _create_default_https_context: Callable[..., SSLContext] -def RAND_bytes(n: int, /) -> bytes: ... - -if sys.version_info < (3, 12): - def RAND_pseudo_bytes(n: int, /) -> tuple[bytes, bool]: ... - -def RAND_status() -> bool: ... -def RAND_egd(path: str) -> None: ... -def RAND_add(string: str | ReadableBuffer, entropy: float, /) -> None: ... - if sys.version_info < (3, 12): def match_hostname(cert: _PeerCertRetDictType, hostname: str) -> None: ... @@ -133,10 +157,6 @@ class DefaultVerifyPaths(NamedTuple): def get_default_verify_paths() -> DefaultVerifyPaths: ... -if sys.platform == "win32": - def enum_certificates(store_name: str) -> _EnumRetType: ... - def enum_crls(store_name: str) -> _EnumRetType: ... - class VerifyMode(enum.IntEnum): CERT_NONE = 0 CERT_OPTIONAL = 1 @@ -229,21 +249,8 @@ if sys.version_info >= (3, 11) or sys.platform == "linux": OP_IGNORE_UNEXPECTED_EOF: Options HAS_NEVER_CHECK_COMMON_NAME: bool -HAS_SSLv2: bool -HAS_SSLv3: bool -HAS_TLSv1: bool -HAS_TLSv1_1: bool -HAS_TLSv1_2: bool -HAS_TLSv1_3: bool -HAS_ALPN: bool -HAS_ECDH: bool -HAS_SNI: bool -HAS_NPN: bool -CHANNEL_BINDING_TYPES: list[str] -OPENSSL_VERSION: str -OPENSSL_VERSION_INFO: tuple[int, int, int, int, int] -OPENSSL_VERSION_NUMBER: int +CHANNEL_BINDING_TYPES: list[str] class AlertDescription(enum.IntEnum): ALERT_DESCRIPTION_ACCESS_DENIED = 49 @@ -302,6 +309,8 @@ ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: AlertDescription ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: AlertDescription ALERT_DESCRIPTION_USER_CANCELLED: AlertDescription +# This class is not exposed. It calls itself ssl._ASN1Object. +@type_check_only class _ASN1ObjectBase(NamedTuple): nid: int shortname: str @@ -379,17 +388,15 @@ class TLSVersion(enum.IntEnum): TLSv1_2 = 771 TLSv1_3 = 772 -class SSLContext: - check_hostname: bool +class SSLContext(_SSLContext): options: Options verify_flags: VerifyFlags verify_mode: VerifyMode @property - def protocol(self) -> _SSLMethod: ... + def protocol(self) -> _SSLMethod: ... # type: ignore[override] hostname_checks_common_name: bool maximum_version: TLSVersion minimum_version: TLSVersion - sni_callback: Callable[[SSLObject, str, SSLContext], None | int] | None # The following two attributes have class-level defaults. # However, the docs explicitly state that it's OK to override these attributes on instances, # so making these ClassVars wouldn't be appropriate @@ -406,10 +413,6 @@ class SSLContext: else: def __new__(cls, protocol: int = ..., *args: Any, **kwargs: Any) -> Self: ... - def cert_store_stats(self) -> dict[str, int]: ... - def load_cert_chain( - self, certfile: StrOrBytesPath, keyfile: StrOrBytesPath | None = None, password: _PasswordType | None = None - ) -> None: ... def load_default_certs(self, purpose: Purpose = ...) -> None: ... def load_verify_locations( self, @@ -448,7 +451,6 @@ class SSLContext: server_hostname: str | bytes | None = None, session: SSLSession | None = None, ) -> SSLObject: ... - def session_stats(self) -> dict[str, int]: ... class SSLObject: context: SSLContext @@ -483,28 +485,6 @@ class SSLObject: def get_verified_chain(self) -> list[bytes]: ... def get_unverified_chain(self) -> list[bytes]: ... -@final -class MemoryBIO: - pending: int - eof: bool - def read(self, size: int = -1, /) -> bytes: ... - def write(self, b: ReadableBuffer, /) -> int: ... - def write_eof(self) -> None: ... - -@final -class SSLSession: - @property - def has_ticket(self) -> bool: ... - @property - def id(self) -> bytes: ... - @property - def ticket_lifetime_hint(self) -> int: ... - @property - def time(self) -> int: ... - @property - def timeout(self) -> int: ... - def __eq__(self, value: object, /) -> bool: ... - class SSLErrorNumber(enum.IntEnum): SSL_ERROR_EOF = 8 SSL_ERROR_INVALID_ERROR_CODE = 10 diff --git a/mypy/typeshed/stdlib/stat.pyi b/mypy/typeshed/stdlib/stat.pyi index f3bdd92c1068a..face28ab0cbb6 100644 --- a/mypy/typeshed/stdlib/stat.pyi +++ b/mypy/typeshed/stdlib/stat.pyi @@ -1,7 +1,7 @@ import sys from _stat import * -from typing import Literal +from typing import Final if sys.version_info >= (3, 13): # https://github.com/python/cpython/issues/114081#issuecomment-2119017790 - SF_RESTRICTED: Literal[0x00080000] + SF_RESTRICTED: Final = 0x00080000 diff --git a/mypy/typeshed/stdlib/struct.pyi b/mypy/typeshed/stdlib/struct.pyi index e684632489ea0..2c26908746ecc 100644 --- a/mypy/typeshed/stdlib/struct.pyi +++ b/mypy/typeshed/stdlib/struct.pyi @@ -1,26 +1,5 @@ -from _typeshed import ReadableBuffer, WriteableBuffer -from collections.abc import Iterator -from typing import Any +from _struct import * __all__ = ["calcsize", "pack", "pack_into", "unpack", "unpack_from", "iter_unpack", "Struct", "error"] class error(Exception): ... - -def pack(fmt: str | bytes, /, *v: Any) -> bytes: ... -def pack_into(fmt: str | bytes, buffer: WriteableBuffer, offset: int, /, *v: Any) -> None: ... -def unpack(format: str | bytes, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... -def unpack_from(format: str | bytes, /, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... -def iter_unpack(format: str | bytes, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... -def calcsize(format: str | bytes, /) -> int: ... - -class Struct: - @property - def format(self) -> str: ... - @property - def size(self) -> int: ... - def __init__(self, format: str | bytes) -> None: ... - def pack(self, *v: Any) -> bytes: ... - def pack_into(self, buffer: WriteableBuffer, offset: int, *v: Any) -> None: ... - def unpack(self, buffer: ReadableBuffer, /) -> tuple[Any, ...]: ... - def unpack_from(self, buffer: ReadableBuffer, offset: int = 0) -> tuple[Any, ...]: ... - def iter_unpack(self, buffer: ReadableBuffer, /) -> Iterator[tuple[Any, ...]]: ... diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index 6234ecc02b483..fef35b56945a3 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -1,8 +1,8 @@ import sys -from _typeshed import ReadableBuffer, StrOrBytesPath +from _typeshed import MaybeNone, ReadableBuffer, StrOrBytesPath from collections.abc import Callable, Collection, Iterable, Mapping, Sequence from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Literal, TypeVar, overload +from typing import IO, Any, AnyStr, Final, Generic, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias if sys.version_info >= (3, 9): @@ -74,8 +74,8 @@ _T = TypeVar("_T") # These two are private but documented if sys.version_info >= (3, 11): - _USE_VFORK: bool -_USE_POSIX_SPAWN: bool + _USE_VFORK: Final[bool] +_USE_POSIX_SPAWN: Final[bool] class CompletedProcess(Generic[_T]): # morally: _CMD @@ -889,6 +889,7 @@ if sys.version_info >= (3, 11): start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, user: str | int | None = None, @@ -920,6 +921,7 @@ elif sys.version_info >= (3, 10): start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, user: str | int | None = None, @@ -950,6 +952,7 @@ elif sys.version_info >= (3, 9): start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, user: str | int | None = None, @@ -978,6 +981,7 @@ else: start_new_session: bool = False, pass_fds: Collection[int] = ..., *, + encoding: str | None = None, timeout: float | None = None, text: bool | None = None, ) -> int: ... @@ -1005,6 +1009,7 @@ if sys.version_info >= (3, 11): pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, user: str | int | None = None, group: str | int | None = None, @@ -1036,6 +1041,7 @@ elif sys.version_info >= (3, 10): pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, user: str | int | None = None, group: str | int | None = None, @@ -1066,6 +1072,7 @@ elif sys.version_info >= (3, 9): pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, user: str | int | None = None, group: str | int | None = None, @@ -1094,6 +1101,7 @@ else: pass_fds: Collection[int] = ..., timeout: float | None = ..., *, + encoding: str | None = None, text: bool | None = None, ) -> int: ... @@ -1802,9 +1810,9 @@ else: text: bool | None = None, ) -> Any: ... # morally: -> str | bytes -PIPE: int -STDOUT: int -DEVNULL: int +PIPE: Final[int] +STDOUT: Final[int] +DEVNULL: Final[int] class SubprocessError(Exception): ... @@ -1840,7 +1848,7 @@ class Popen(Generic[AnyStr]): stdout: IO[AnyStr] | None stderr: IO[AnyStr] | None pid: int - returncode: int | Any + returncode: int | MaybeNone universal_newlines: bool if sys.version_info >= (3, 11): @@ -2574,6 +2582,11 @@ else: def list2cmdline(seq: Iterable[StrOrBytesPath]) -> str: ... # undocumented if sys.platform == "win32": + if sys.version_info >= (3, 13): + from _winapi import STARTF_FORCEOFFFEEDBACK, STARTF_FORCEONFEEDBACK + + __all__ += ["STARTF_FORCEOFFFEEDBACK", "STARTF_FORCEONFEEDBACK"] + class STARTUPINFO: def __init__( self, diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi index 0f080954ba2c2..ee0a1eb2f1cb5 100644 --- a/mypy/typeshed/stdlib/symtable.pyi +++ b/mypy/typeshed/stdlib/symtable.pyi @@ -2,14 +2,34 @@ import sys from _collections_abc import dict_keys from collections.abc import Sequence from typing import Any +from typing_extensions import deprecated __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"] +if sys.version_info >= (3, 13): + __all__ += ["SymbolTableType"] + def symtable(code: str, filename: str, compile_type: str) -> SymbolTable: ... +if sys.version_info >= (3, 13): + from enum import StrEnum + + class SymbolTableType(StrEnum): + MODULE = "module" + FUNCTION = "function" + CLASS = "class" + ANNOTATION = "annotation" + TYPE_ALIAS = "type alias" + TYPE_PARAMETERS = "type parameters" + TYPE_VARIABLE = "type variable" + class SymbolTable: def __init__(self, raw_table: Any, filename: str) -> None: ... - def get_type(self) -> str: ... + if sys.version_info >= (3, 13): + def get_type(self) -> SymbolTableType: ... + else: + def get_type(self) -> str: ... + def get_id(self) -> int: ... def get_name(self) -> str: ... def get_lineno(self) -> int: ... @@ -32,7 +52,9 @@ class Function(SymbolTable): def get_nonlocals(self) -> tuple[str, ...]: ... class Class(SymbolTable): - def get_methods(self) -> tuple[str, ...]: ... + if sys.version_info < (3, 16): + @deprecated("deprecated in Python 3.14, will be removed in Python 3.16") + def get_methods(self) -> tuple[str, ...]: ... class Symbol: def __init__( @@ -42,13 +64,23 @@ class Symbol: def get_name(self) -> str: ... def is_referenced(self) -> bool: ... def is_parameter(self) -> bool: ... + if sys.version_info >= (3, 14): + def is_type_parameter(self) -> bool: ... + def is_global(self) -> bool: ... def is_declared_global(self) -> bool: ... def is_local(self) -> bool: ... def is_annotated(self) -> bool: ... def is_free(self) -> bool: ... + if sys.version_info >= (3, 14): + def is_free_class(self) -> bool: ... + def is_imported(self) -> bool: ... def is_assigned(self) -> bool: ... + if sys.version_info >= (3, 14): + def is_comp_iter(self) -> bool: ... + def is_comp_cell(self) -> bool: ... + def is_namespace(self) -> bool: ... def get_namespaces(self) -> Sequence[SymbolTable]: ... def get_namespace(self) -> SymbolTable: ... diff --git a/mypy/typeshed/stdlib/sys/__init__.pyi b/mypy/typeshed/stdlib/sys/__init__.pyi index 9989a27b2bc17..c4b1adca9bc6b 100644 --- a/mypy/typeshed/stdlib/sys/__init__.pyi +++ b/mypy/typeshed/stdlib/sys/__init__.pyi @@ -5,14 +5,13 @@ from builtins import object as _object from collections.abc import AsyncGenerator, Callable, Sequence from io import TextIOWrapper from types import FrameType, ModuleType, TracebackType -from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final +from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final, type_check_only from typing_extensions import TypeAlias _T = TypeVar("_T") # see https://github.com/python/typeshed/issues/8513#issue-1333671093 for the rationale behind this alias _ExitCode: TypeAlias = str | int | None -_OptExcInfo: TypeAlias = OptExcInfo # noqa: Y047 # TODO: obsolete, remove fall 2022 or later # ----- sys variables ----- if sys.platform != "win32": @@ -90,13 +89,63 @@ _UninstantiableStructseq: TypeAlias = structseq[Any] flags: _flags -if sys.version_info >= (3, 10): - _FlagTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int, int] -else: - _FlagTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int] - +# This class is not exposed at runtime. It calls itself sys.flags. +# As a tuple, it can have a length between 15 and 18. We don't model +# the exact length here because that varies by patch version due to +# the backported security fix int_max_str_digits. The exact length shouldn't +# be relied upon. See #13031 +# This can be re-visited when typeshed drops support for 3.10, +# at which point all supported versions will include int_max_str_digits +# in all patch versions. +# 3.8 and 3.9 are 15 or 16-tuple +# 3.10 is 16 or 17-tuple +# 3.11+ is an 18-tuple. @final -class _flags(_UninstantiableStructseq, _FlagTuple): +@type_check_only +class _flags(_UninstantiableStructseq, tuple[int, ...]): + # `safe_path` was added in py311 + if sys.version_info >= (3, 11): + __match_args__: Final = ( + "debug", + "inspect", + "interactive", + "optimize", + "dont_write_bytecode", + "no_user_site", + "no_site", + "ignore_environment", + "verbose", + "bytes_warning", + "quiet", + "hash_randomization", + "isolated", + "dev_mode", + "utf8_mode", + "warn_default_encoding", + "safe_path", + "int_max_str_digits", + ) + elif sys.version_info >= (3, 10): + __match_args__: Final = ( + "debug", + "inspect", + "interactive", + "optimize", + "dont_write_bytecode", + "no_user_site", + "no_site", + "ignore_environment", + "verbose", + "bytes_warning", + "quiet", + "hash_randomization", + "isolated", + "dev_mode", + "utf8_mode", + "warn_default_encoding", + "int_max_str_digits", + ) + @property def debug(self) -> int: ... @property @@ -129,15 +178,39 @@ class _flags(_UninstantiableStructseq, _FlagTuple): def utf8_mode(self) -> int: ... if sys.version_info >= (3, 10): @property - def warn_default_encoding(self) -> int: ... # undocumented + def warn_default_encoding(self) -> int: ... if sys.version_info >= (3, 11): @property def safe_path(self) -> bool: ... + # Whether or not this exists on lower versions of Python + # may depend on which patch release you're using + # (it was backported to all Python versions on 3.8+ as a security fix) + # Added in: 3.8.14, 3.9.14, 3.10.7 + # and present in all versions of 3.11 and later. + @property + def int_max_str_digits(self) -> int: ... float_info: _float_info +# This class is not exposed at runtime. It calls itself sys.float_info. @final +@type_check_only class _float_info(structseq[float], tuple[float, int, int, float, int, int, int, int, float, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ( + "max", + "max_exp", + "max_10_exp", + "min", + "min_exp", + "min_10_exp", + "dig", + "mant_dig", + "epsilon", + "radix", + "rounds", + ) + @property def max(self) -> float: ... # DBL_MAX @property @@ -163,8 +236,13 @@ class _float_info(structseq[float], tuple[float, int, int, float, int, int, int, hash_info: _hash_info +# This class is not exposed at runtime. It calls itself sys.hash_info. @final +@type_check_only class _hash_info(structseq[Any | int], tuple[int, int, int, int, int, str, int, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("width", "modulus", "inf", "nan", "imag", "algorithm", "hash_bits", "seed_bits", "cutoff") + @property def width(self) -> int: ... @property @@ -186,6 +264,9 @@ class _hash_info(structseq[Any | int], tuple[int, int, int, int, int, str, int, implementation: _implementation +# This class isn't really a thing. At runtime, implementation is an instance +# of types.SimpleNamespace. This allows for better typing. +@type_check_only class _implementation: name: str version: _version_info @@ -198,8 +279,13 @@ class _implementation: int_info: _int_info +# This class is not exposed at runtime. It calls itself sys.int_info. @final +@type_check_only class _int_info(structseq[int], tuple[int, int, int, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("bits_per_digit", "sizeof_digit", "default_max_str_digits", "str_digits_check_threshold") + @property def bits_per_digit(self) -> int: ... @property @@ -212,8 +298,13 @@ class _int_info(structseq[int], tuple[int, int, int, int]): _ThreadInfoName: TypeAlias = Literal["nt", "pthread", "pthread-stubs", "solaris"] _ThreadInfoLock: TypeAlias = Literal["semaphore", "mutex+cond"] | None +# This class is not exposed at runtime. It calls itself sys.thread_info. @final +@type_check_only class _thread_info(_UninstantiableStructseq, tuple[_ThreadInfoName, _ThreadInfoLock, str | None]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("name", "lock", "version") + @property def name(self) -> _ThreadInfoName: ... @property @@ -224,8 +315,13 @@ class _thread_info(_UninstantiableStructseq, tuple[_ThreadInfoName, _ThreadInfoL thread_info: _thread_info _ReleaseLevel: TypeAlias = Literal["alpha", "beta", "candidate", "final"] +# This class is not exposed at runtime. It calls itself sys.version_info. @final +@type_check_only class _version_info(_UninstantiableStructseq, tuple[int, int, int, _ReleaseLevel, int]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("major", "minor", "micro", "releaselevel", "serial") + @property def major(self) -> int: ... @property @@ -318,6 +414,7 @@ if sys.version_info < (3, 9): def callstats() -> tuple[int, int, int, int, int, int, int, int, int, int, int] | None: ... # Doesn't exist at runtime, but exported in the stubs so pytest etc. can annotate their code more easily. +@type_check_only class UnraisableHookArgs(Protocol): exc_type: type[BaseException] exc_value: BaseException | None @@ -333,8 +430,13 @@ def audit(event: str, /, *args: Any) -> None: ... _AsyncgenHook: TypeAlias = Callable[[AsyncGenerator[Any, Any]], None] | None +# This class is not exposed at runtime. It calls itself builtins.asyncgen_hooks. @final +@type_check_only class _asyncgen_hooks(structseq[_AsyncgenHook], tuple[_AsyncgenHook, _AsyncgenHook]): + if sys.version_info >= (3, 10): + __match_args__: Final = ("firstiter", "finalizer") + @property def firstiter(self) -> _AsyncgenHook: ... @property @@ -355,7 +457,11 @@ def set_int_max_str_digits(maxdigits: int) -> None: ... def get_int_max_str_digits() -> int: ... if sys.version_info >= (3, 12): - def getunicodeinternedsize() -> int: ... + if sys.version_info >= (3, 13): + def getunicodeinternedsize(*, _only_immortal: bool = False) -> int: ... + else: + def getunicodeinternedsize() -> int: ... + def deactivate_stack_trampoline() -> None: ... def is_stack_trampoline_active() -> bool: ... # It always exists, but raises on non-linux platforms: diff --git a/mypy/typeshed/stdlib/syslog.pyi b/mypy/typeshed/stdlib/syslog.pyi index d539dd5e4579f..1e0d0d3839022 100644 --- a/mypy/typeshed/stdlib/syslog.pyi +++ b/mypy/typeshed/stdlib/syslog.pyi @@ -1,48 +1,50 @@ import sys -from typing import Literal, overload +from typing import Final, overload if sys.platform != "win32": - LOG_ALERT: Literal[1] - LOG_AUTH: Literal[32] - LOG_AUTHPRIV: Literal[80] - LOG_CONS: Literal[2] - LOG_CRIT: Literal[2] - LOG_CRON: Literal[72] - LOG_DAEMON: Literal[24] - LOG_DEBUG: Literal[7] - LOG_EMERG: Literal[0] - LOG_ERR: Literal[3] - LOG_INFO: Literal[6] - LOG_KERN: Literal[0] - LOG_LOCAL0: Literal[128] - LOG_LOCAL1: Literal[136] - LOG_LOCAL2: Literal[144] - LOG_LOCAL3: Literal[152] - LOG_LOCAL4: Literal[160] - LOG_LOCAL5: Literal[168] - LOG_LOCAL6: Literal[176] - LOG_LOCAL7: Literal[184] - LOG_LPR: Literal[48] - LOG_MAIL: Literal[16] - LOG_NDELAY: Literal[8] - LOG_NEWS: Literal[56] - LOG_NOTICE: Literal[5] - LOG_NOWAIT: Literal[16] - LOG_ODELAY: Literal[4] - LOG_PERROR: Literal[32] - LOG_PID: Literal[1] - LOG_SYSLOG: Literal[40] - LOG_USER: Literal[8] - LOG_UUCP: Literal[64] - LOG_WARNING: Literal[4] + LOG_ALERT: Final = 1 + LOG_AUTH: Final = 32 + LOG_AUTHPRIV: Final = 80 + LOG_CONS: Final = 2 + LOG_CRIT: Final = 2 + LOG_CRON: Final = 72 + LOG_DAEMON: Final = 24 + LOG_DEBUG: Final = 7 + LOG_EMERG: Final = 0 + LOG_ERR: Final = 3 + LOG_INFO: Final = 6 + LOG_KERN: Final = 0 + LOG_LOCAL0: Final = 128 + LOG_LOCAL1: Final = 136 + LOG_LOCAL2: Final = 144 + LOG_LOCAL3: Final = 152 + LOG_LOCAL4: Final = 160 + LOG_LOCAL5: Final = 168 + LOG_LOCAL6: Final = 176 + LOG_LOCAL7: Final = 184 + LOG_LPR: Final = 48 + LOG_MAIL: Final = 16 + LOG_NDELAY: Final = 8 + LOG_NEWS: Final = 56 + LOG_NOTICE: Final = 5 + LOG_NOWAIT: Final = 16 + LOG_ODELAY: Final = 4 + LOG_PERROR: Final = 32 + LOG_PID: Final = 1 + LOG_SYSLOG: Final = 40 + LOG_USER: Final = 8 + LOG_UUCP: Final = 64 + LOG_WARNING: Final = 4 if sys.version_info >= (3, 13): - LOG_FTP: Literal[88] - LOG_INSTALL: Literal[112] - LOG_LAUNCHD: Literal[192] - LOG_NETINFO: Literal[96] - LOG_RAS: Literal[120] - LOG_REMOTEAUTH: Literal[104] + LOG_FTP: Final = 88 + + if sys.platform == "darwin": + LOG_INSTALL: Final = 112 + LOG_LAUNCHD: Final = 192 + LOG_NETINFO: Final = 96 + LOG_RAS: Final = 120 + LOG_REMOTEAUTH: Final = 104 def LOG_MASK(pri: int, /) -> int: ... def LOG_UPTO(pri: int, /) -> int: ... diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index e520994641744..a7135d8150ee9 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -1,7 +1,7 @@ import bz2 import io import sys -from _typeshed import StrOrBytesPath, StrPath +from _typeshed import StrOrBytesPath, StrPath, SupportsRead from builtins import list as _list # aliases to avoid name clashes with fields named "type" or "list" from collections.abc import Callable, Iterable, Iterator, Mapping from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj @@ -103,10 +103,11 @@ PAX_NAME_FIELDS: set[str] ENCODING: str +@overload def open( name: StrOrBytesPath | None = None, - mode: str = "r", - fileobj: IO[bytes] | None = None, # depends on mode + mode: Literal["r", "r:*", "r:", "r:gz", "r:bz2", "r:xz"] = "r", + fileobj: IO[bytes] | None = None, bufsize: int = 10240, *, format: int | None = ..., @@ -118,10 +119,136 @@ def open( pax_headers: Mapping[str, str] | None = ..., debug: int | None = ..., errorlevel: int | None = ..., - compresslevel: int | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None, + mode: Literal["x", "x:", "a", "a:", "w", "w:"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None = None, + *, + mode: Literal["x", "x:", "a", "a:", "w", "w:"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None, + mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int = 9, +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None = None, + *, + mode: Literal["x:gz", "x:bz2", "w:gz", "w:bz2"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + compresslevel: int = 9, +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None, + mode: Literal["x:xz", "w:xz"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + *, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., +) -> TarFile: ... +@overload +def open( + name: StrOrBytesPath | None = None, + *, + mode: Literal["x:xz", "w:xz"], + fileobj: _Fileobj | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., preset: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | None = ..., ) -> TarFile: ... +# TODO: Temporary fallback for modes containing pipe characters. These don't +# work with mypy 1.10, but this should be fixed with mypy 1.11. +# https://github.com/python/typeshed/issues/12182 +@overload +def open( + name: StrOrBytesPath | None = None, + *, + mode: str, + fileobj: IO[bytes] | None = None, + bufsize: int = 10240, + format: int | None = ..., + tarinfo: type[TarInfo] | None = ..., + dereference: bool | None = ..., + ignore_zeros: bool | None = ..., + encoding: str | None = ..., + errors: str = ..., + pax_headers: Mapping[str, str] | None = ..., + debug: int | None = ..., + errorlevel: int | None = ..., + preset: int | None = ..., +) -> TarFile: ... + class ExFileObject(io.BufferedReader): def __init__(self, tarfile: TarFile, tarinfo: TarInfo) -> None: ... @@ -354,7 +481,7 @@ class TarFile: *, filter: Callable[[TarInfo], TarInfo | None] | None = None, ) -> None: ... - def addfile(self, tarinfo: TarInfo, fileobj: IO[bytes] | None = None) -> None: ... + def addfile(self, tarinfo: TarInfo, fileobj: SupportsRead[bytes] | None = None) -> None: ... def gettarinfo( self, name: StrOrBytesPath | None = None, arcname: str | None = None, fileobj: IO[bytes] | None = None ) -> TarInfo: ... @@ -401,7 +528,7 @@ class TarInfo: name: str path: str size: int - mtime: int + mtime: int | float chksum: int devmajor: int devminor: int diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 3ae8cca39f77e..0c19d56fc7a65 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -253,17 +253,19 @@ class _TemporaryFileWrapper(IO[AnyStr]): def truncate(self, size: int | None = ...) -> int: ... def writable(self) -> bool: ... @overload - def write(self: _TemporaryFileWrapper[str], s: str) -> int: ... + def write(self: _TemporaryFileWrapper[str], s: str, /) -> int: ... @overload - def write(self: _TemporaryFileWrapper[bytes], s: ReadableBuffer) -> int: ... + def write(self: _TemporaryFileWrapper[bytes], s: ReadableBuffer, /) -> int: ... @overload - def write(self, s: AnyStr) -> int: ... + def write(self, s: AnyStr, /) -> int: ... @overload def writelines(self: _TemporaryFileWrapper[str], lines: Iterable[str]) -> None: ... @overload def writelines(self: _TemporaryFileWrapper[bytes], lines: Iterable[ReadableBuffer]) -> None: ... @overload def writelines(self, lines: Iterable[AnyStr]) -> None: ... + @property + def closed(self) -> bool: ... if sys.version_info >= (3, 11): _SpooledTemporaryFileBase = io.IOBase @@ -461,7 +463,7 @@ class TemporaryDirectory(Generic[AnyStr]): # The overloads overlap, but they should still work fine. @overload -def mkstemp( # type: ignore[overload-overlap] +def mkstemp( suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None, text: bool = False ) -> tuple[int, str]: ... @overload @@ -471,7 +473,7 @@ def mkstemp( # The overloads overlap, but they should still work fine. @overload -def mkdtemp(suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None) -> str: ... # type: ignore[overload-overlap] +def mkdtemp(suffix: str | None = None, prefix: str | None = None, dir: StrPath | None = None) -> str: ... @overload def mkdtemp(suffix: bytes | None = None, prefix: bytes | None = None, dir: BytesPath | None = None) -> bytes: ... def mktemp(suffix: str = "", prefix: str = "tmp", dir: StrPath | None = None) -> str: ... diff --git a/mypy/typeshed/stdlib/termios.pyi b/mypy/typeshed/stdlib/termios.pyi index a5378e40fdf2c..5a5a1f53be3c6 100644 --- a/mypy/typeshed/stdlib/termios.pyi +++ b/mypy/typeshed/stdlib/termios.pyi @@ -10,58 +10,38 @@ _AttrReturn: TypeAlias = list[Any] if sys.platform != "win32": B0: int - B1000000: int B110: int B115200: int - B1152000: int B1200: int B134: int B150: int - B1500000: int B1800: int B19200: int B200: int - B2000000: int B230400: int B2400: int - B2500000: int B300: int - B3000000: int - B3500000: int B38400: int - B4000000: int - B460800: int B4800: int B50: int - B500000: int B57600: int - B576000: int B600: int B75: int - B921600: int B9600: int BRKINT: int BS0: int BS1: int BSDLY: int - CBAUD: int - CBAUDEX: int - CDEL: int CDSUSP: int CEOF: int CEOL: int - CEOL2: int CEOT: int CERASE: int - CESC: int CFLUSH: int - CIBAUD: int CINTR: int CKILL: int CLNEXT: int CLOCAL: int - CNUL: int - COMMON: int CQUIT: int CR0: int CR1: int @@ -80,7 +60,6 @@ if sys.platform != "win32": CSTOP: int CSTOPB: int CSUSP: int - CSWTCH: int CWERASE: int ECHO: int ECHOCTL: int @@ -101,7 +80,6 @@ if sys.platform != "win32": FIONREAD: int FLUSHO: int HUPCL: int - IBSHIFT: int ICANON: int ICRNL: int IEXTEN: int @@ -109,33 +87,21 @@ if sys.platform != "win32": IGNCR: int IGNPAR: int IMAXBEL: int - INIT_C_CC: int INLCR: int INPCK: int - IOCSIZE_MASK: int - IOCSIZE_SHIFT: int ISIG: int ISTRIP: int - IUCLC: int IXANY: int IXOFF: int IXON: int - N_MOUSE: int - N_PPP: int - N_SLIP: int - N_STRIP: int - N_TTY: int - NCC: int NCCS: int NL0: int NL1: int NLDLY: int NOFLSH: int - NSWTCH: int OCRNL: int OFDEL: int OFILL: int - OLCUC: int ONLCR: int ONLRET: int ONOCR: int @@ -149,9 +115,6 @@ if sys.platform != "win32": TAB2: int TAB3: int TABDLY: int - TCFLSH: int - TCGETA: int - TCGETS: int TCIFLUSH: int TCIOFF: int TCIOFLUSH: int @@ -162,28 +125,11 @@ if sys.platform != "win32": TCSADRAIN: int TCSAFLUSH: int TCSANOW: int - TCSASOFT: int - TCSBRK: int - TCSBRKP: int - TCSETA: int - TCSETAF: int - TCSETAW: int - TCSETS: int - TCSETSF: int - TCSETSW: int - TCXONC: int TIOCCONS: int TIOCEXCL: int TIOCGETD: int - TIOCGICOUNT: int - TIOCGLCKTRMIOS: int TIOCGPGRP: int - TIOCGSERIAL: int - TIOCGSIZE: int - TIOCGSOFTCAR: int TIOCGWINSZ: int - TIOCINQ: int - TIOCLINUX: int TIOCM_CAR: int TIOCM_CD: int TIOCM_CTS: int @@ -198,7 +144,6 @@ if sys.platform != "win32": TIOCMBIC: int TIOCMBIS: int TIOCMGET: int - TIOCMIWAIT: int TIOCMSET: int TIOCNOTTY: int TIOCNXCL: int @@ -212,23 +157,10 @@ if sys.platform != "win32": TIOCPKT_STOP: int TIOCPKT: int TIOCSCTTY: int - TIOCSER_TEMT: int - TIOCSERCONFIG: int - TIOCSERGETLSR: int - TIOCSERGETMULTI: int - TIOCSERGSTRUCT: int - TIOCSERGWILD: int - TIOCSERSETMULTI: int - TIOCSERSWILD: int TIOCSETD: int - TIOCSLCKTRMIOS: int TIOCSPGRP: int - TIOCSSERIAL: int - TIOCSSIZE: int - TIOCSSOFTCAR: int TIOCSTI: int TIOCSWINSZ: int - TIOCTTYGSTRUCT: int TOSTOP: int VDISCARD: int VEOF: int @@ -244,15 +176,119 @@ if sys.platform != "win32": VSTART: int VSTOP: int VSUSP: int - VSWTC: int - VSWTCH: int VT0: int VT1: int VTDLY: int VTIME: int VWERASE: int - XCASE: int - XTABS: int + + if sys.version_info >= (3, 13): + EXTPROC: int + IUTF8: int + + if sys.platform == "darwin" and sys.version_info >= (3, 13): + ALTWERASE: int + B14400: int + B28800: int + B7200: int + B76800: int + CCAR_OFLOW: int + CCTS_OFLOW: int + CDSR_OFLOW: int + CDTR_IFLOW: int + CIGNORE: int + CRTS_IFLOW: int + MDMBUF: int + NL2: int + NL3: int + NOKERNINFO: int + ONOEOT: int + OXTABS: int + VDSUSP: int + VSTATUS: int + + if sys.platform == "darwin" and sys.version_info >= (3, 11): + TIOCGSIZE: int + TIOCSSIZE: int + + if sys.platform == "linux": + B1152000: int + B576000: int + CBAUD: int + CBAUDEX: int + CIBAUD: int + IOCSIZE_MASK: int + IOCSIZE_SHIFT: int + IUCLC: int + N_MOUSE: int + N_PPP: int + N_SLIP: int + N_STRIP: int + N_TTY: int + NCC: int + OLCUC: int + TCFLSH: int + TCGETA: int + TCGETS: int + TCSBRK: int + TCSBRKP: int + TCSETA: int + TCSETAF: int + TCSETAW: int + TCSETS: int + TCSETSF: int + TCSETSW: int + TCXONC: int + TIOCGICOUNT: int + TIOCGLCKTRMIOS: int + TIOCGSERIAL: int + TIOCGSOFTCAR: int + TIOCINQ: int + TIOCLINUX: int + TIOCMIWAIT: int + TIOCTTYGSTRUCT: int + TIOCSER_TEMT: int + TIOCSERCONFIG: int + TIOCSERGETLSR: int + TIOCSERGETMULTI: int + TIOCSERGSTRUCT: int + TIOCSERGWILD: int + TIOCSERSETMULTI: int + TIOCSERSWILD: int + TIOCSLCKTRMIOS: int + TIOCSSERIAL: int + TIOCSSOFTCAR: int + VSWTC: int + VSWTCH: int + XCASE: int + XTABS: int + + if sys.platform != "darwin": + B1000000: int + B1500000: int + B2000000: int + B2500000: int + B3000000: int + B3500000: int + B4000000: int + B460800: int + B500000: int + B921600: int + + if sys.platform != "linux": + TCSASOFT: int + + if sys.platform != "darwin" and sys.platform != "linux": + # not available on FreeBSD either. + CDEL: int + CEOL2: int + CESC: int + CNUL: int + COMMON: int + CSWTCH: int + IBSHIFT: int + INIT_C_CC: int + NSWTCH: int def tcgetattr(fd: FileDescriptorLike, /) -> _AttrReturn: ... def tcsetattr(fd: FileDescriptorLike, when: int, attributes: _Attr, /) -> None: ... diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index 1ecadef508d00..c441a04681e22 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -61,7 +61,7 @@ if sys.version_info >= (3, 10): def gettrace() -> TraceFunction | None: ... def getprofile() -> ProfileFunction | None: ... -def stack_size(size: int = ...) -> int: ... +def stack_size(size: int = 0, /) -> int: ... TIMEOUT_MAX: float diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index d8ce17535eab2..d6a234d67919c 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -1,13 +1,18 @@ import _tkinter import sys -from _typeshed import Incomplete, StrEnum, StrOrBytesPath -from collections.abc import Callable, Mapping, Sequence +from _typeshed import Incomplete, MaybeNone, StrOrBytesPath +from collections.abc import Callable, Iterable, Mapping, Sequence from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType -from typing import Any, Generic, Literal, NamedTuple, TypedDict, TypeVar, overload, type_check_only +from typing import Any, Generic, Literal, NamedTuple, Protocol, TypedDict, TypeVar, overload, type_check_only from typing_extensions import TypeAlias, TypeVarTuple, Unpack, deprecated +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from enum import Enum + if sys.version_info >= (3, 9): __all__ = [ "TclError", @@ -186,53 +191,99 @@ _XYScrollCommand: TypeAlias = str | Callable[[float, float], object] _TakeFocusValue: TypeAlias = bool | Literal[0, 1, ""] | Callable[[str], bool | None] # -takefocus in manual page named 'options' if sys.version_info >= (3, 11): - class _VersionInfoType(NamedTuple): + @type_check_only + class _VersionInfoTypeBase(NamedTuple): major: int minor: int micro: int releaselevel: str serial: int -class EventType(StrEnum): - Activate = "36" - ButtonPress = "4" - Button = ButtonPress - ButtonRelease = "5" - Circulate = "26" - CirculateRequest = "27" - ClientMessage = "33" - Colormap = "32" - Configure = "22" - ConfigureRequest = "23" - Create = "16" - Deactivate = "37" - Destroy = "17" - Enter = "7" - Expose = "12" - FocusIn = "9" - FocusOut = "10" - GraphicsExpose = "13" - Gravity = "24" - KeyPress = "2" - Key = "2" - KeyRelease = "3" - Keymap = "11" - Leave = "8" - Map = "19" - MapRequest = "20" - Mapping = "34" - Motion = "6" - MouseWheel = "38" - NoExpose = "14" - Property = "28" - Reparent = "21" - ResizeRequest = "25" - Selection = "31" - SelectionClear = "29" - SelectionRequest = "30" - Unmap = "18" - VirtualEvent = "35" - Visibility = "15" + class _VersionInfoType(_VersionInfoTypeBase): ... + +if sys.version_info >= (3, 11): + class EventType(StrEnum): + Activate = "36" + ButtonPress = "4" + Button = ButtonPress + ButtonRelease = "5" + Circulate = "26" + CirculateRequest = "27" + ClientMessage = "33" + Colormap = "32" + Configure = "22" + ConfigureRequest = "23" + Create = "16" + Deactivate = "37" + Destroy = "17" + Enter = "7" + Expose = "12" + FocusIn = "9" + FocusOut = "10" + GraphicsExpose = "13" + Gravity = "24" + KeyPress = "2" + Key = "2" + KeyRelease = "3" + Keymap = "11" + Leave = "8" + Map = "19" + MapRequest = "20" + Mapping = "34" + Motion = "6" + MouseWheel = "38" + NoExpose = "14" + Property = "28" + Reparent = "21" + ResizeRequest = "25" + Selection = "31" + SelectionClear = "29" + SelectionRequest = "30" + Unmap = "18" + VirtualEvent = "35" + Visibility = "15" + +else: + class EventType(str, Enum): + Activate = "36" + ButtonPress = "4" + Button = ButtonPress + ButtonRelease = "5" + Circulate = "26" + CirculateRequest = "27" + ClientMessage = "33" + Colormap = "32" + Configure = "22" + ConfigureRequest = "23" + Create = "16" + Deactivate = "37" + Destroy = "17" + Enter = "7" + Expose = "12" + FocusIn = "9" + FocusOut = "10" + GraphicsExpose = "13" + Gravity = "24" + KeyPress = "2" + Key = "2" + KeyRelease = "3" + Keymap = "11" + Leave = "8" + Map = "19" + MapRequest = "20" + Mapping = "34" + Motion = "6" + MouseWheel = "38" + NoExpose = "14" + Property = "28" + Reparent = "21" + ResizeRequest = "25" + Selection = "31" + SelectionClear = "29" + SelectionRequest = "30" + Unmap = "18" + VirtualEvent = "35" + Visibility = "15" _W = TypeVar("_W", bound=Misc) # Events considered covariant because you should never assign to event.widget. @@ -509,7 +560,7 @@ class Misc: pad: _ScreenUnits = ..., uniform: str = ..., weight: int = ..., - ) -> _GridIndexInfo | Any: ... # can be None but annoying to check + ) -> _GridIndexInfo | MaybeNone: ... # can be None but annoying to check def grid_rowconfigure( self, index: int | str | list[int] | tuple[int, ...], @@ -519,7 +570,7 @@ class Misc: pad: _ScreenUnits = ..., uniform: str = ..., weight: int = ..., - ) -> _GridIndexInfo | Any: ... # can be None but annoying to check + ) -> _GridIndexInfo | MaybeNone: ... # can be None but annoying to check columnconfigure = grid_columnconfigure rowconfigure = grid_rowconfigure def grid_location(self, x: _ScreenUnits, y: _ScreenUnits) -> tuple[int, int]: ... @@ -576,7 +627,8 @@ class Misc: def __getitem__(self, key: str) -> Any: ... def cget(self, key: str) -> Any: ... def configure(self, cnf: Any = None) -> Any: ... - # TODO: config is an alias of configure, but adding that here creates lots of mypy errors + # TODO: config is an alias of configure, but adding that here creates + # conflict with the type of config in the subclasses. See #13149 class CallWrapper: func: Incomplete @@ -2148,11 +2200,12 @@ class Listbox(Widget, XView, YView): selectborderwidth: _ScreenUnits = 0, selectforeground: str = ..., # from listbox man page: "The value of the [selectmode] option may be - # arbitrary, but the default bindings expect it to be ..." + # arbitrary, but the default bindings expect it to be either single, + # browse, multiple, or extended" # # I have never seen anyone setting this to something else than what # "the default bindings expect", but let's support it anyway. - selectmode: str = "browse", + selectmode: str | Literal["single", "browse", "multiple", "extended"] = "browse", # noqa: Y051 setgrid: bool = False, state: Literal["normal", "disabled"] = "normal", takefocus: _TakeFocusValue = "", @@ -2187,7 +2240,7 @@ class Listbox(Widget, XView, YView): selectbackground: str = ..., selectborderwidth: _ScreenUnits = ..., selectforeground: str = ..., - selectmode: str = ..., + selectmode: str | Literal["single", "browse", "multiple", "extended"] = ..., # noqa: Y051 setgrid: bool = ..., state: Literal["normal", "disabled"] = ..., takefocus: _TakeFocusValue = ..., @@ -2907,6 +2960,9 @@ class Scrollbar(Widget): def set(self, first: float | str, last: float | str) -> None: ... _TextIndex: TypeAlias = _tkinter.Tcl_Obj | str | float | Misc +_WhatToCount: TypeAlias = Literal[ + "chars", "displaychars", "displayindices", "displaylines", "indices", "lines", "xpixels", "ypixels" +] class Text(Widget, XView, YView): def __init__( @@ -3021,7 +3077,133 @@ class Text(Widget, XView, YView): config = configure def bbox(self, index: _TextIndex) -> tuple[int, int, int, int] | None: ... # type: ignore[override] def compare(self, index1: _TextIndex, op: Literal["<", "<=", "==", ">=", ">", "!="], index2: _TextIndex) -> bool: ... - def count(self, index1, index2, *args): ... # TODO + if sys.version_info >= (3, 13): + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, *, return_ints: Literal[True]) -> int: ... + @overload + def count( + self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], /, *, return_ints: Literal[True] + ) -> int: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: Literal["update"], + arg2: _WhatToCount, + /, + *, + return_ints: Literal[True], + ) -> int: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount, + arg2: Literal["update"], + /, + *, + return_ints: Literal[True], + ) -> int: ... + @overload + def count( + self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /, *, return_ints: Literal[True] + ) -> tuple[int, int]: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount | Literal["update"], + arg2: _WhatToCount | Literal["update"], + arg3: _WhatToCount | Literal["update"], + /, + *args: _WhatToCount | Literal["update"], + return_ints: Literal[True], + ) -> tuple[int, ...]: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, *, return_ints: Literal[False] = False) -> tuple[int] | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg: _WhatToCount | Literal["update"], + /, + *, + return_ints: Literal[False] = False, + ) -> tuple[int] | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: Literal["update"], + arg2: _WhatToCount, + /, + *, + return_ints: Literal[False] = False, + ) -> int | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount, + arg2: Literal["update"], + /, + *, + return_ints: Literal[False] = False, + ) -> int | None: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount, + arg2: _WhatToCount, + /, + *, + return_ints: Literal[False] = False, + ) -> tuple[int, int]: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount | Literal["update"], + arg2: _WhatToCount | Literal["update"], + arg3: _WhatToCount | Literal["update"], + /, + *args: _WhatToCount | Literal["update"], + return_ints: Literal[False] = False, + ) -> tuple[int, ...]: ... + else: + @overload + def count(self, index1: _TextIndex, index2: _TextIndex) -> tuple[int] | None: ... + @overload + def count( + self, index1: _TextIndex, index2: _TextIndex, arg: _WhatToCount | Literal["update"], / + ) -> tuple[int] | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: Literal["update"], arg2: _WhatToCount, /) -> int | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: Literal["update"], /) -> int | None: ... + @overload + def count(self, index1: _TextIndex, index2: _TextIndex, arg1: _WhatToCount, arg2: _WhatToCount, /) -> tuple[int, int]: ... + @overload + def count( + self, + index1: _TextIndex, + index2: _TextIndex, + arg1: _WhatToCount | Literal["update"], + arg2: _WhatToCount | Literal["update"], + arg3: _WhatToCount | Literal["update"], + /, + *args: _WhatToCount | Literal["update"], + ) -> tuple[int, ...]: ... + @overload def debug(self, boolean: None = None) -> bool: ... @overload @@ -3273,11 +3455,14 @@ class OptionMenu(Menubutton): # configure, config, cget are inherited from Menubutton # destroy and __getitem__ are overridden, signature does not change -# Marker to indicate that it is a valid bitmap/photo image. PIL implements compatible versions -# which don't share a class hierarchy. The actual API is a __str__() which returns a valid name, -# not something that type checkers can detect. +# This matches tkinter's image classes (PhotoImage and BitmapImage) +# and PIL's tkinter-compatible class (PIL.ImageTk.PhotoImage), +# but not a plain PIL image that isn't tkinter compatible. +# The reason is that PIL has width and height attributes, not methods. @type_check_only -class _Image: ... +class _Image(Protocol): + def width(self) -> int: ... + def height(self) -> int: ... @type_check_only class _BitmapImageLike(_Image): ... @@ -3296,9 +3481,7 @@ class Image(_Image): def __getitem__(self, key): ... configure: Incomplete config: Incomplete - def height(self) -> int: ... def type(self): ... - def width(self) -> int: ... class PhotoImage(Image, _PhotoImageLike): # This should be kept in sync with PIL.ImageTK.PhotoImage.__init__() @@ -3331,9 +3514,33 @@ class PhotoImage(Image, _PhotoImageLike): def blank(self) -> None: ... def cget(self, option: str) -> str: ... def __getitem__(self, key: str) -> str: ... # always string: image['height'] can be '0' - def copy(self) -> PhotoImage: ... - def zoom(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... - def subsample(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... + if sys.version_info >= (3, 13): + def copy( + self, + *, + from_coords: Iterable[int] | None = None, + zoom: int | tuple[int, int] | list[int] | None = None, + subsample: int | tuple[int, int] | list[int] | None = None, + ) -> PhotoImage: ... + def subsample(self, x: int, y: Literal[""] = "", *, from_coords: Iterable[int] | None = None) -> PhotoImage: ... + def zoom(self, x: int, y: Literal[""] = "", *, from_coords: Iterable[int] | None = None) -> PhotoImage: ... + def copy_replace( + self, + sourceImage: PhotoImage | str, + *, + from_coords: Iterable[int] | None = None, + to: Iterable[int] | None = None, + shrink: bool = False, + zoom: int | tuple[int, int] | list[int] | None = None, + subsample: int | tuple[int, int] | list[int] | None = None, + # `None` defaults to overlay. + compositingrule: Literal["overlay", "set"] | None = None, + ) -> None: ... + else: + def copy(self) -> PhotoImage: ... + def zoom(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... + def subsample(self, x: int, y: int | Literal[""] = "") -> PhotoImage: ... + def get(self, x: int, y: int) -> tuple[int, int, int]: ... def put( self, @@ -3348,7 +3555,44 @@ class PhotoImage(Image, _PhotoImageLike): ), to: tuple[int, int] | None = None, ) -> None: ... - def write(self, filename: StrOrBytesPath, format: str | None = None, from_coords: tuple[int, int] | None = None) -> None: ... + if sys.version_info >= (3, 13): + def read( + self, + filename: StrOrBytesPath, + format: str | None = None, + *, + from_coords: Iterable[int] | None = None, + to: Iterable[int] | None = None, + shrink: bool = False, + ) -> None: ... + def write( + self, + filename: StrOrBytesPath, + format: str | None = None, + from_coords: Iterable[int] | None = None, + *, + background: str | None = None, + grayscale: bool = False, + ) -> None: ... + @overload + def data( + self, format: str, *, from_coords: Iterable[int] | None = None, background: str | None = None, grayscale: bool = False + ) -> bytes: ... + @overload + def data( + self, + format: None = None, + *, + from_coords: Iterable[int] | None = None, + background: str | None = None, + grayscale: bool = False, + ) -> tuple[str, ...]: ... + + else: + def write( + self, filename: StrOrBytesPath, format: str | None = None, from_coords: tuple[int, int] | None = None + ) -> None: ... + def transparency_get(self, x: int, y: int) -> bool: ... def transparency_set(self, x: int, y: int, boolean: bool) -> None: ... @@ -3503,7 +3747,7 @@ class Spinbox(Widget, XView): def scan_dragto(self, x): ... def selection(self, *args) -> tuple[int, ...]: ... def selection_adjust(self, index): ... - def selection_clear(self): ... + def selection_clear(self): ... # type: ignore[override] def selection_element(self, element: Incomplete | None = None): ... def selection_from(self, index: int) -> None: ... def selection_present(self) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/constants.pyi b/mypy/typeshed/stdlib/tkinter/constants.pyi index 74fa72acb0bfb..fbfe8b49b9979 100644 --- a/mypy/typeshed/stdlib/tkinter/constants.pyi +++ b/mypy/typeshed/stdlib/tkinter/constants.pyi @@ -1,80 +1,80 @@ -from typing import Literal +from typing import Final # These are not actually bools. See #4669 -NO: bool -YES: bool -TRUE: bool -FALSE: bool -ON: bool -OFF: bool -N: Literal["n"] -S: Literal["s"] -W: Literal["w"] -E: Literal["e"] -NW: Literal["nw"] -SW: Literal["sw"] -NE: Literal["ne"] -SE: Literal["se"] -NS: Literal["ns"] -EW: Literal["ew"] -NSEW: Literal["nsew"] -CENTER: Literal["center"] -NONE: Literal["none"] -X: Literal["x"] -Y: Literal["y"] -BOTH: Literal["both"] -LEFT: Literal["left"] -TOP: Literal["top"] -RIGHT: Literal["right"] -BOTTOM: Literal["bottom"] -RAISED: Literal["raised"] -SUNKEN: Literal["sunken"] -FLAT: Literal["flat"] -RIDGE: Literal["ridge"] -GROOVE: Literal["groove"] -SOLID: Literal["solid"] -HORIZONTAL: Literal["horizontal"] -VERTICAL: Literal["vertical"] -NUMERIC: Literal["numeric"] -CHAR: Literal["char"] -WORD: Literal["word"] -BASELINE: Literal["baseline"] -INSIDE: Literal["inside"] -OUTSIDE: Literal["outside"] -SEL: Literal["sel"] -SEL_FIRST: Literal["sel.first"] -SEL_LAST: Literal["sel.last"] -END: Literal["end"] -INSERT: Literal["insert"] -CURRENT: Literal["current"] -ANCHOR: Literal["anchor"] -ALL: Literal["all"] -NORMAL: Literal["normal"] -DISABLED: Literal["disabled"] -ACTIVE: Literal["active"] -HIDDEN: Literal["hidden"] -CASCADE: Literal["cascade"] -CHECKBUTTON: Literal["checkbutton"] -COMMAND: Literal["command"] -RADIOBUTTON: Literal["radiobutton"] -SEPARATOR: Literal["separator"] -SINGLE: Literal["single"] -BROWSE: Literal["browse"] -MULTIPLE: Literal["multiple"] -EXTENDED: Literal["extended"] -DOTBOX: Literal["dotbox"] -UNDERLINE: Literal["underline"] -PIESLICE: Literal["pieslice"] -CHORD: Literal["chord"] -ARC: Literal["arc"] -FIRST: Literal["first"] -LAST: Literal["last"] -BUTT: Literal["butt"] -PROJECTING: Literal["projecting"] -ROUND: Literal["round"] -BEVEL: Literal["bevel"] -MITER: Literal["miter"] -MOVETO: Literal["moveto"] -SCROLL: Literal["scroll"] -UNITS: Literal["units"] -PAGES: Literal["pages"] +NO: Final[bool] +YES: Final[bool] +TRUE: Final[bool] +FALSE: Final[bool] +ON: Final[bool] +OFF: Final[bool] +N: Final = "n" +S: Final = "s" +W: Final = "w" +E: Final = "e" +NW: Final = "nw" +SW: Final = "sw" +NE: Final = "ne" +SE: Final = "se" +NS: Final = "ns" +EW: Final = "ew" +NSEW: Final = "nsew" +CENTER: Final = "center" +NONE: Final = "none" +X: Final = "x" +Y: Final = "y" +BOTH: Final = "both" +LEFT: Final = "left" +TOP: Final = "top" +RIGHT: Final = "right" +BOTTOM: Final = "bottom" +RAISED: Final = "raised" +SUNKEN: Final = "sunken" +FLAT: Final = "flat" +RIDGE: Final = "ridge" +GROOVE: Final = "groove" +SOLID: Final = "solid" +HORIZONTAL: Final = "horizontal" +VERTICAL: Final = "vertical" +NUMERIC: Final = "numeric" +CHAR: Final = "char" +WORD: Final = "word" +BASELINE: Final = "baseline" +INSIDE: Final = "inside" +OUTSIDE: Final = "outside" +SEL: Final = "sel" +SEL_FIRST: Final = "sel.first" +SEL_LAST: Final = "sel.last" +END: Final = "end" +INSERT: Final = "insert" +CURRENT: Final = "current" +ANCHOR: Final = "anchor" +ALL: Final = "all" +NORMAL: Final = "normal" +DISABLED: Final = "disabled" +ACTIVE: Final = "active" +HIDDEN: Final = "hidden" +CASCADE: Final = "cascade" +CHECKBUTTON: Final = "checkbutton" +COMMAND: Final = "command" +RADIOBUTTON: Final = "radiobutton" +SEPARATOR: Final = "separator" +SINGLE: Final = "single" +BROWSE: Final = "browse" +MULTIPLE: Final = "multiple" +EXTENDED: Final = "extended" +DOTBOX: Final = "dotbox" +UNDERLINE: Final = "underline" +PIESLICE: Final = "pieslice" +CHORD: Final = "chord" +ARC: Final = "arc" +FIRST: Final = "first" +LAST: Final = "last" +BUTT: Final = "butt" +PROJECTING: Final = "projecting" +ROUND: Final = "round" +BEVEL: Final = "bevel" +MITER: Final = "miter" +MOVETO: Final = "moveto" +SCROLL: Final = "scroll" +UNITS: Final = "units" +PAGES: Final = "pages" diff --git a/mypy/typeshed/stdlib/tkinter/dialog.pyi b/mypy/typeshed/stdlib/tkinter/dialog.pyi index f76732a254608..b7d74c0fa71e5 100644 --- a/mypy/typeshed/stdlib/tkinter/dialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/dialog.pyi @@ -2,12 +2,12 @@ import sys from _typeshed import Incomplete from collections.abc import Mapping from tkinter import Widget -from typing import Any +from typing import Any, Final if sys.version_info >= (3, 9): __all__ = ["Dialog"] -DIALOG_ICON: str +DIALOG_ICON: Final = "questhead" class Dialog(Widget): widgetName: str diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 46625014d4ac4..097c2e4b43826 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -1,16 +1,17 @@ import _tkinter +import itertools import sys import tkinter -from typing import Any, Literal, TypedDict, overload +from typing import Any, ClassVar, Final, Literal, TypedDict, overload from typing_extensions import TypeAlias if sys.version_info >= (3, 9): __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] -NORMAL: Literal["normal"] -ROMAN: Literal["roman"] -BOLD: Literal["bold"] -ITALIC: Literal["italic"] +NORMAL: Final = "normal" +ROMAN: Final = "roman" +BOLD: Final = "bold" +ITALIC: Final = "italic" _FontDescription: TypeAlias = ( str # "Helvetica 12" @@ -40,6 +41,7 @@ class _MetricsDict(TypedDict): class Font: name: str delete_font: bool + counter: ClassVar[itertools.count[int]] # undocumented def __init__( self, # In tkinter, 'root' refers to tkinter.Tk by convention, but the code diff --git a/mypy/typeshed/stdlib/tkinter/messagebox.pyi b/mypy/typeshed/stdlib/tkinter/messagebox.pyi index 5a04b66d7866e..5cdfe512f9b76 100644 --- a/mypy/typeshed/stdlib/tkinter/messagebox.pyi +++ b/mypy/typeshed/stdlib/tkinter/messagebox.pyi @@ -1,6 +1,6 @@ import sys from tkinter.commondialog import Dialog -from typing import ClassVar +from typing import ClassVar, Final if sys.version_info >= (3, 9): __all__ = [ @@ -14,22 +14,22 @@ if sys.version_info >= (3, 9): "askretrycancel", ] -ERROR: str -INFO: str -QUESTION: str -WARNING: str -ABORTRETRYIGNORE: str -OK: str -OKCANCEL: str -RETRYCANCEL: str -YESNO: str -YESNOCANCEL: str -ABORT: str -RETRY: str -IGNORE: str -CANCEL: str -YES: str -NO: str +ERROR: Final = "error" +INFO: Final = "info" +QUESTION: Final = "question" +WARNING: Final = "warning" +ABORTRETRYIGNORE: Final = "abortretryignore" +OK: Final = "ok" +OKCANCEL: Final = "okcancel" +RETRYCANCEL: Final = "retrycancel" +YESNO: Final = "yesno" +YESNOCANCEL: Final = "yesnocancel" +ABORT: Final = "abort" +RETRY: Final = "retry" +IGNORE: Final = "ignore" +CANCEL: Final = "cancel" +YES: Final = "yes" +NO: Final = "no" class Message(Dialog): command: ClassVar[str] diff --git a/mypy/typeshed/stdlib/tkinter/tix.pyi b/mypy/typeshed/stdlib/tkinter/tix.pyi index 73649de427e85..7891364fa02c6 100644 --- a/mypy/typeshed/stdlib/tkinter/tix.pyi +++ b/mypy/typeshed/stdlib/tkinter/tix.pyi @@ -1,38 +1,38 @@ import tkinter from _typeshed import Incomplete -from typing import Any, Literal - -WINDOW: Literal["window"] -TEXT: Literal["text"] -STATUS: Literal["status"] -IMMEDIATE: Literal["immediate"] -IMAGE: Literal["image"] -IMAGETEXT: Literal["imagetext"] -BALLOON: Literal["balloon"] -AUTO: Literal["auto"] -ACROSSTOP: Literal["acrosstop"] - -ASCII: Literal["ascii"] -CELL: Literal["cell"] -COLUMN: Literal["column"] -DECREASING: Literal["decreasing"] -INCREASING: Literal["increasing"] -INTEGER: Literal["integer"] -MAIN: Literal["main"] -MAX: Literal["max"] -REAL: Literal["real"] -ROW: Literal["row"] -S_REGION: Literal["s-region"] -X_REGION: Literal["x-region"] -Y_REGION: Literal["y-region"] +from typing import Any, Final + +WINDOW: Final = "window" +TEXT: Final = "text" +STATUS: Final = "status" +IMMEDIATE: Final = "immediate" +IMAGE: Final = "image" +IMAGETEXT: Final = "imagetext" +BALLOON: Final = "balloon" +AUTO: Final = "auto" +ACROSSTOP: Final = "acrosstop" + +ASCII: Final = "ascii" +CELL: Final = "cell" +COLUMN: Final = "column" +DECREASING: Final = "decreasing" +INCREASING: Final = "increasing" +INTEGER: Final = "integer" +MAIN: Final = "main" +MAX: Final = "max" +REAL: Final = "real" +ROW: Final = "row" +S_REGION: Final = "s-region" +X_REGION: Final = "x-region" +Y_REGION: Final = "y-region" # These should be kept in sync with _tkinter constants, except TCL_ALL_EVENTS which doesn't match ALL_EVENTS -TCL_DONT_WAIT: Literal[2] -TCL_WINDOW_EVENTS: Literal[4] -TCL_FILE_EVENTS: Literal[8] -TCL_TIMER_EVENTS: Literal[16] -TCL_IDLE_EVENTS: Literal[32] -TCL_ALL_EVENTS: Literal[0] +TCL_DONT_WAIT: Final = 2 +TCL_WINDOW_EVENTS: Final = 4 +TCL_FILE_EVENTS: Final = 8 +TCL_TIMER_EVENTS: Final = 16 +TCL_IDLE_EVENTS: Final = 32 +TCL_ALL_EVENTS: Final = 0 class tixCommand: def tix_addbitmapdir(self, directory: str) -> None: ... diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index 86a23ce822115..dacef0620b22c 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1,6 +1,6 @@ import _tkinter import tkinter -from _typeshed import Incomplete +from _typeshed import Incomplete, MaybeNone from collections.abc import Callable from tkinter.font import _FontDescription from typing import Any, Literal, TypedDict, overload @@ -556,7 +556,9 @@ class Notebook(Widget): sticky: str = ..., # consists of letters 'n', 's', 'w', 'e', no repeats, may be empty padding: _Padding = ..., text: str = ..., - image=..., # Sequence of an image name, followed by zero or more (sequences of one or more state names followed by an image name) + # `image` is a sequence of an image name, followed by zero or more + # (sequences of one or more state names followed by an image name) + image=..., compound: tkinter._Compound = ..., underline: int = ..., ) -> None: ... @@ -1040,7 +1042,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def heading(self, column: str | int, option: str) -> Any: ... @overload - def heading(self, column: str | int, option: None = None) -> _TreeviewHeaderDict: ... # type: ignore[overload-overlap] + def heading(self, column: str | int, option: None = None) -> _TreeviewHeaderDict: ... @overload def heading( self, @@ -1052,7 +1054,8 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): anchor: tkinter._Anchor = ..., command: str | Callable[[], object] = ..., ) -> None: ... - def identify(self, component, x, y): ... # Internal Method. Leave untyped + # Internal Method. Leave untyped: + def identify(self, component, x, y): ... # type: ignore[override] def identify_row(self, y: int) -> str: ... def identify_column(self, x: int) -> str: ... def identify_region(self, x: int, y: int) -> Literal["heading", "separator", "tree", "cell", "nothing"]: ... @@ -1084,7 +1087,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def item(self, item: str | int, option: str) -> Any: ... @overload - def item(self, item: str | int, option: None = None) -> _TreeviewItemDict: ... # type: ignore[overload-overlap] + def item(self, item: str | int, option: None = None) -> _TreeviewItemDict: ... @overload def item( self, @@ -1153,7 +1156,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): background: str = ..., font: _FontDescription = ..., image: tkinter._ImageSpec = ..., - ) -> _TreeviewTagDict | Any: ... # can be None but annoying to check + ) -> _TreeviewTagDict | MaybeNone: ... # can be None but annoying to check @overload def tag_has(self, tagname: str, item: None = None) -> tuple[str, ...]: ... @overload diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 3d2a93865df8e..7e9a945cdc46d 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -4,7 +4,7 @@ from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern from token import * from token import EXACT_TOKEN_TYPES as EXACT_TOKEN_TYPES -from typing import Any, NamedTuple, TextIO +from typing import Any, NamedTuple, TextIO, type_check_only from typing_extensions import TypeAlias __all__ = [ @@ -98,6 +98,8 @@ blank_re: Pattern[bytes] _Position: TypeAlias = tuple[int, int] +# This class is not exposed. It calls itself tokenize.TokenInfo. +@type_check_only class _TokenInfo(NamedTuple): type: int string: str @@ -133,7 +135,7 @@ class Untokenizer: def untokenize(iterable: Iterable[_Token]) -> Any: ... def detect_encoding(readline: Callable[[], bytes | bytearray]) -> tuple[str, Sequence[bytes]]: ... def tokenize(readline: Callable[[], bytes | bytearray]) -> Generator[TokenInfo, None, None]: ... -def generate_tokens(readline: Callable[[], str]) -> Generator[TokenInfo, None, None]: ... # undocumented +def generate_tokens(readline: Callable[[], str]) -> Generator[TokenInfo, None, None]: ... def open(filename: FileDescriptorOrPath) -> TextIO: ... def group(*choices: str) -> str: ... # undocumented def any(*choices: str) -> str: ... # undocumented diff --git a/mypy/typeshed/stdlib/trace.pyi b/mypy/typeshed/stdlib/trace.pyi index d32647a55cb59..04390f1191951 100644 --- a/mypy/typeshed/stdlib/trace.pyi +++ b/mypy/typeshed/stdlib/trace.pyi @@ -27,7 +27,18 @@ class CoverageResults: outfile: StrPath | None = None, ) -> None: ... # undocumented def update(self, other: CoverageResults) -> None: ... - def write_results(self, show_missing: bool = True, summary: bool = False, coverdir: StrPath | None = None) -> None: ... + if sys.version_info >= (3, 13): + def write_results( + self, + show_missing: bool = True, + summary: bool = False, + coverdir: StrPath | None = None, + *, + ignore_missing_files: bool = False, + ) -> None: ... + else: + def write_results(self, show_missing: bool = True, summary: bool = False, coverdir: StrPath | None = None) -> None: ... + def write_results_file( self, path: StrPath, lines: Sequence[str], lnotab: Any, lines_hit: Mapping[int, int], encoding: str | None = None ) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 075c0f4b9de8f..1c4a59de66aa2 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -231,7 +231,7 @@ class TracebackException: if sys.version_info >= (3, 11): def print(self, *, file: SupportsWrite[str] | None = None, chain: bool = True) -> None: ... -class FrameSummary(Iterable[Any]): +class FrameSummary: if sys.version_info >= (3, 11): def __init__( self, @@ -276,6 +276,8 @@ class FrameSummary(Iterable[Any]): def __getitem__(self, pos: Literal[3]) -> str | None: ... @overload def __getitem__(self, pos: int) -> Any: ... + @overload + def __getitem__(self, pos: slice) -> tuple[Any, ...]: ... def __iter__(self) -> Iterator[Any]: ... def __eq__(self, other: object) -> bool: ... def __len__(self) -> Literal[4]: ... diff --git a/mypy/typeshed/stdlib/tty.pyi b/mypy/typeshed/stdlib/tty.pyi index add0d57a8d4b1..0611879cf1b29 100644 --- a/mypy/typeshed/stdlib/tty.pyi +++ b/mypy/typeshed/stdlib/tty.pyi @@ -1,6 +1,6 @@ import sys import termios -from typing import IO +from typing import IO, Final from typing_extensions import TypeAlias if sys.platform != "win32": @@ -15,13 +15,13 @@ if sys.platform != "win32": _FD: TypeAlias = int | IO[str] # XXX: Undocumented integer constants - IFLAG: int - OFLAG: int - CFLAG: int - LFLAG: int - ISPEED: int - OSPEED: int - CC: int + IFLAG: Final[int] + OFLAG: Final[int] + CFLAG: Final[int] + LFLAG: Final[int] + ISPEED: Final[int] + OSPEED: Final[int] + CC: Final[int] def setraw(fd: _FD, when: int = 2) -> _ModeSetterReturn: ... def setcbreak(fd: _FD, when: int = 2) -> _ModeSetterReturn: ... diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index fd0723fd73ed6..a2ab728de943f 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -1,7 +1,7 @@ import sys from collections.abc import Callable, Sequence from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar -from typing import Any, ClassVar, overload +from typing import Any, ClassVar, Literal, TypedDict, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -101,7 +101,6 @@ __all__ = [ "setheading", "setpos", "setposition", - "settiltangle", "setundobuffer", "setx", "sety", @@ -132,6 +131,9 @@ __all__ = [ if sys.version_info >= (3, 12): __all__ += ["teleport"] +if sys.version_info < (3, 13): + __all__ += ["settiltangle"] + # Note: '_Color' is the alias we use for arguments and _AnyColor is the # alias we use for return types. Really, these two aliases should be the # same, but as per the "no union returns" typeshed policy, we'll return @@ -139,8 +141,18 @@ if sys.version_info >= (3, 12): _Color: TypeAlias = str | tuple[float, float, float] _AnyColor: TypeAlias = Any -# TODO: Replace this with a TypedDict once it becomes standardized. -_PenState: TypeAlias = dict[str, Any] +class _PenState(TypedDict): + shown: bool + pendown: bool + pencolor: _Color + fillcolor: _Color + pensize: int + speed: int + resizemode: Literal["auto", "user", "noresize"] + stretchfactor: tuple[float, float] + shearfactor: float + outline: int + tilt: float _Speed: TypeAlias = str | float _PolygonCoords: TypeAlias = Sequence[tuple[float, float]] @@ -281,6 +293,7 @@ class TNavigator: def heading(self) -> float: ... def setheading(self, to_angle: float) -> None: ... def circle(self, radius: float, extent: float | None = None, steps: int | None = None) -> None: ... + def speed(self, s: int | None = 0) -> int | None: ... fd = forward bk = back backward = back @@ -336,7 +349,7 @@ class TPen: def isvisible(self) -> bool: ... # Note: signatures 1 and 2 overlap unsafely when no arguments are provided @overload - def pen(self) -> _PenState: ... # type: ignore[overload-overlap] + def pen(self) -> _PenState: ... @overload def pen( self, @@ -361,7 +374,7 @@ class TPen: st = showturtle ht = hideturtle -class RawTurtle(TPen, TNavigator): +class RawTurtle(TPen, TNavigator): # type: ignore[misc] # Conflicting methods in base classes screen: TurtleScreen screens: ClassVar[list[TurtleScreen]] def __init__( @@ -382,7 +395,7 @@ class RawTurtle(TPen, TNavigator): def shape(self, name: str) -> None: ... # Unsafely overlaps when no arguments are provided @overload - def shapesize(self) -> tuple[float, float, float]: ... # type: ignore[overload-overlap] + def shapesize(self) -> tuple[float, float, float]: ... @overload def shapesize( self, stretch_wid: float | None = None, stretch_len: float | None = None, outline: float | None = None @@ -393,13 +406,16 @@ class RawTurtle(TPen, TNavigator): def shearfactor(self, shear: float) -> None: ... # Unsafely overlaps when no arguments are provided @overload - def shapetransform(self) -> tuple[float, float, float, float]: ... # type: ignore[overload-overlap] + def shapetransform(self) -> tuple[float, float, float, float]: ... @overload def shapetransform( self, t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None ) -> None: ... def get_shapepoly(self) -> _PolygonCoords | None: ... - def settiltangle(self, angle: float) -> None: ... + + if sys.version_info < (3, 13): + def settiltangle(self, angle: float) -> None: ... + @overload def tiltangle(self, angle: None = None) -> float: ... @overload @@ -617,7 +633,7 @@ def isvisible() -> bool: ... # Note: signatures 1 and 2 overlap unsafely when no arguments are provided @overload -def pen() -> _PenState: ... # type: ignore[overload-overlap] +def pen() -> _PenState: ... @overload def pen( pen: _PenState | None = None, @@ -656,7 +672,7 @@ if sys.version_info >= (3, 12): # Unsafely overlaps when no arguments are provided @overload -def shapesize() -> tuple[float, float, float]: ... # type: ignore[overload-overlap] +def shapesize() -> tuple[float, float, float]: ... @overload def shapesize(stretch_wid: float | None = None, stretch_len: float | None = None, outline: float | None = None) -> None: ... @overload @@ -666,13 +682,16 @@ def shearfactor(shear: float) -> None: ... # Unsafely overlaps when no arguments are provided @overload -def shapetransform() -> tuple[float, float, float, float]: ... # type: ignore[overload-overlap] +def shapetransform() -> tuple[float, float, float, float]: ... @overload def shapetransform( t11: float | None = None, t12: float | None = None, t21: float | None = None, t22: float | None = None ) -> None: ... def get_shapepoly() -> _PolygonCoords | None: ... -def settiltangle(angle: float) -> None: ... + +if sys.version_info < (3, 13): + def settiltangle(angle: float) -> None: ... + @overload def tiltangle(angle: None = None) -> float: ... @overload diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 9e9dc56b85299..b513bd77468a4 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -1,5 +1,5 @@ import sys -from _typeshed import SupportsKeysAndGetItem +from _typeshed import MaybeNone, SupportsKeysAndGetItem from _typeshed.importlib import LoaderProtocol from collections.abc import ( AsyncGenerator, @@ -245,7 +245,7 @@ class CodeType: co_qualname: str = ..., co_linetable: bytes = ..., co_exceptiontable: bytes = ..., - ) -> CodeType: ... + ) -> Self: ... elif sys.version_info >= (3, 10): def replace( self, @@ -266,7 +266,7 @@ class CodeType: co_filename: str = ..., co_name: str = ..., co_linetable: bytes = ..., - ) -> CodeType: ... + ) -> Self: ... else: def replace( self, @@ -287,7 +287,10 @@ class CodeType: co_filename: str = ..., co_name: str = ..., co_lnotab: bytes = ..., - ) -> CodeType: ... + ) -> Self: ... + + if sys.version_info >= (3, 13): + __replace__ = replace @final class MappingProxyType(Mapping[_KT, _VT_co]): @@ -301,6 +304,10 @@ class MappingProxyType(Mapping[_KT, _VT_co]): def keys(self) -> KeysView[_KT]: ... def values(self) -> ValuesView[_VT_co]: ... def items(self) -> ItemsView[_KT, _VT_co]: ... + @overload + def get(self, key: _KT, /) -> _VT_co | None: ... + @overload + def get(self, key: _KT, default: _VT_co | _T2, /) -> _VT_co | _T2: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def __reversed__(self) -> Iterator[_KT]: ... @@ -309,11 +316,17 @@ class MappingProxyType(Mapping[_KT, _VT_co]): class SimpleNamespace: __hash__: ClassVar[None] # type: ignore[assignment] - def __init__(self, **kwargs: Any) -> None: ... + if sys.version_info >= (3, 13): + def __init__(self, mapping_or_iterable: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), /, **kwargs: Any) -> None: ... + else: + def __init__(self, **kwargs: Any) -> None: ... + def __eq__(self, value: object, /) -> bool: ... def __getattribute__(self, name: str, /) -> Any: ... def __setattr__(self, name: str, value: Any, /) -> None: ... def __delattr__(self, name: str, /) -> None: ... + if sys.version_info >= (3, 13): + def __replace__(self, **kwargs: Any) -> Self: ... class ModuleType: __name__: str @@ -324,6 +337,13 @@ class ModuleType: __package__: str | None __path__: MutableSequence[str] __spec__: ModuleSpec | None + # N.B. Although this is the same type as `builtins.object.__doc__`, + # it is deliberately redeclared here. Most symbols declared in the namespace + # of `types.ModuleType` are available as "implicit globals" within a module's + # namespace, but this is not true for symbols declared in the namespace of `builtins.object`. + # Redeclaring `__doc__` here helps some type checkers understand that `__doc__` is available + # as an implicit global in all modules, similar to `__name__`, `__file__`, `__spec__`, etc. + __doc__: str | None def __init__(self, name: str, doc: str | None = ...) -> None: ... # __getattr__ doesn't exist at runtime, # but having it here in typeshed makes dynamic imports @@ -411,6 +431,8 @@ class MethodType: @property def __closure__(self) -> tuple[CellType, ...] | None: ... # inherited from the added function @property + def __code__(self) -> CodeType: ... # inherited from the added function + @property def __defaults__(self) -> tuple[Any, ...] | None: ... # inherited from the added function @property def __func__(self) -> Callable[..., Any]: ... @@ -513,9 +535,9 @@ class FrameType: def f_lasti(self) -> int: ... # see discussion in #6769: f_lineno *can* sometimes be None, # but you should probably file a bug report with CPython if you encounter it being None in the wild. - # An `int | None` annotation here causes too many false-positive errors. + # An `int | None` annotation here causes too many false-positive errors, so applying `int | Any`. @property - def f_lineno(self) -> int | Any: ... + def f_lineno(self) -> int | MaybeNone: ... @property def f_locals(self) -> dict[str, Any]: ... f_trace: Callable[[FrameType, str, Any], Any] | None @@ -570,7 +592,7 @@ _P = ParamSpec("_P") # it's not really an Awaitable, but can be used in an await expression. Real type: Generator & Awaitable @overload -def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ... # type: ignore[overload-overlap] +def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ... @overload def coroutine(func: _Fn) -> _Fn: ... diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 92427f91f022a..8f0d4fbb6a024 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -2,7 +2,7 @@ # ruff: noqa: F811 # TODO: The collections import is required, otherwise mypy crashes. # https://github.com/python/mypy/issues/16744 -import collections # noqa: F401 # pyright: ignore +import collections # noqa: F401 # pyright: ignore[reportUnusedImport] import sys import typing_extensions from _collections_abc import dict_items, dict_keys, dict_values @@ -133,6 +133,8 @@ if sys.version_info >= (3, 13): Any = object() +class _Final: ... + def final(f: _T) -> _T: ... @final class TypeVar: @@ -191,7 +193,7 @@ _promote = object() # N.B. Keep this definition in sync with typing_extensions._SpecialForm @final -class _SpecialForm: +class _SpecialForm(_Final): def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... @@ -211,8 +213,7 @@ Tuple: _SpecialForm Final: _SpecialForm Literal: _SpecialForm -# TypedDict is a (non-subscriptable) special form. -TypedDict: object +TypedDict: _SpecialForm if sys.version_info >= (3, 11): Self: _SpecialForm @@ -462,7 +463,11 @@ class Generator(Iterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra, _Return @overload @abstractmethod def throw(self, typ: BaseException, val: None = None, tb: TracebackType | None = None, /) -> _YieldT_co: ... - def close(self) -> None: ... + if sys.version_info >= (3, 13): + def close(self) -> _ReturnT_co | None: ... + else: + def close(self) -> None: ... + def __iter__(self) -> Generator[_YieldT_co, _SendT_contra, _ReturnT_co]: ... @property def gi_code(self) -> CodeType: ... @@ -540,18 +545,20 @@ class AsyncIterator(AsyncIterable[_T_co], Protocol[_T_co]): def __aiter__(self) -> AsyncIterator[_T_co]: ... class AsyncGenerator(AsyncIterator[_YieldT_co], Generic[_YieldT_co, _SendT_contra]): - def __anext__(self) -> Awaitable[_YieldT_co]: ... + def __anext__(self) -> Coroutine[Any, Any, _YieldT_co]: ... @abstractmethod - def asend(self, value: _SendT_contra, /) -> Awaitable[_YieldT_co]: ... + def asend(self, value: _SendT_contra, /) -> Coroutine[Any, Any, _YieldT_co]: ... @overload @abstractmethod def athrow( self, typ: type[BaseException], val: BaseException | object = None, tb: TracebackType | None = None, / - ) -> Awaitable[_YieldT_co]: ... + ) -> Coroutine[Any, Any, _YieldT_co]: ... @overload @abstractmethod - def athrow(self, typ: BaseException, val: None = None, tb: TracebackType | None = None, /) -> Awaitable[_YieldT_co]: ... - def aclose(self) -> Awaitable[None]: ... + def athrow( + self, typ: BaseException, val: None = None, tb: TracebackType | None = None, / + ) -> Coroutine[Any, Any, _YieldT_co]: ... + def aclose(self) -> Coroutine[Any, Any, None]: ... @property def ag_await(self) -> Any: ... @property @@ -573,7 +580,7 @@ class Collection(Iterable[_T_co], Container[_T_co], Protocol[_T_co]): @abstractmethod def __len__(self) -> int: ... -class Sequence(Collection[_T_co], Reversible[_T_co]): +class Sequence(Reversible[_T_co], Collection[_T_co]): @overload @abstractmethod def __getitem__(self, index: int) -> _T_co: ... @@ -657,7 +664,6 @@ class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, def __rand__(self, other: Iterable[_T]) -> set[_T]: ... def __contains__(self, item: object) -> bool: ... def __iter__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... - def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... def __or__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... def __ror__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... def __sub__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ... @@ -671,7 +677,6 @@ class KeysView(MappingView, AbstractSet[_KT_co]): def __rand__(self, other: Iterable[_T]) -> set[_T]: ... def __contains__(self, key: object) -> bool: ... def __iter__(self) -> Iterator[_KT_co]: ... - def __reversed__(self) -> Iterator[_KT_co]: ... def __or__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... def __ror__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... def __sub__(self, other: Iterable[Any]) -> set[_KT_co]: ... @@ -683,7 +688,6 @@ class ValuesView(MappingView, Collection[_VT_co]): def __init__(self, mapping: Mapping[Any, _VT_co]) -> None: ... # undocumented def __contains__(self, value: object) -> bool: ... def __iter__(self) -> Iterator[_VT_co]: ... - def __reversed__(self) -> Iterator[_VT_co]: ... class Mapping(Collection[_KT], Generic[_KT, _VT_co]): # TODO: We wish the key type could also be covariant, but that doesn't work, @@ -758,7 +762,7 @@ TYPE_CHECKING: bool # In stubs, the arguments of the IO class are marked as positional-only. # This differs from runtime, but better reflects the fact that in reality # classes deriving from IO use different names for the arguments. -class IO(Iterator[AnyStr]): +class IO(Generic[AnyStr]): # At runtime these are all abstract properties, # but making them abstract in the stub is hugely disruptive, for not much gain. # See #8726 @@ -798,18 +802,12 @@ class IO(Iterator[AnyStr]): def writable(self) -> bool: ... @abstractmethod @overload - def write(self: IO[str], s: str, /) -> int: ... - @abstractmethod - @overload def write(self: IO[bytes], s: ReadableBuffer, /) -> int: ... @abstractmethod @overload def write(self, s: AnyStr, /) -> int: ... @abstractmethod @overload - def writelines(self: IO[str], lines: Iterable[str], /) -> None: ... - @abstractmethod - @overload def writelines(self: IO[bytes], lines: Iterable[ReadableBuffer], /) -> None: ... @abstractmethod @overload @@ -844,7 +842,8 @@ class TextIO(IO[str]): @abstractmethod def __enter__(self) -> TextIO: ... -ByteString: typing_extensions.TypeAlias = bytes | bytearray | memoryview +if sys.version_info < (3, 14): + ByteString: typing_extensions.TypeAlias = bytes | bytearray | memoryview # Functions @@ -864,13 +863,13 @@ if sys.version_info >= (3, 9): def get_type_hints( obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, - localns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, include_extras: bool = False, ) -> dict[str, Any]: ... else: def get_type_hints( - obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None + obj: _get_type_hints_obj_allowed_types, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None ) -> dict[str, Any]: ... def get_args(tp: Any) -> tuple[Any, ...]: ... @@ -976,7 +975,7 @@ class _TypedDict(Mapping[str, object], metaclass=ABCMeta): def __ior__(self, value: typing_extensions.Self, /) -> typing_extensions.Self: ... # type: ignore[misc] @final -class ForwardRef: +class ForwardRef(_Final): __forward_arg__: str __forward_code__: CodeType __forward_evaluated__: bool @@ -998,13 +997,13 @@ class ForwardRef: "that references a PEP 695 type parameter. It will be disallowed in Python 3.15." ) def _evaluate( - self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None, *, recursive_guard: frozenset[str] + self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, *, recursive_guard: frozenset[str] ) -> Any | None: ... @overload def _evaluate( self, globalns: dict[str, Any] | None, - localns: dict[str, Any] | None, + localns: Mapping[str, Any] | None, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...], *, recursive_guard: frozenset[str], @@ -1013,17 +1012,17 @@ class ForwardRef: def _evaluate( self, globalns: dict[str, Any] | None, - localns: dict[str, Any] | None, + localns: Mapping[str, Any] | None, type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] | None = None, *, recursive_guard: frozenset[str], ) -> Any | None: ... elif sys.version_info >= (3, 9): def _evaluate( - self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None, recursive_guard: frozenset[str] + self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None, recursive_guard: frozenset[str] ) -> Any | None: ... else: - def _evaluate(self, globalns: dict[str, Any] | None, localns: dict[str, Any] | None) -> Any | None: ... + def _evaluate(self, globalns: dict[str, Any] | None, localns: Mapping[str, Any] | None) -> Any | None: ... def __eq__(self, other: object) -> bool: ... def __hash__(self) -> int: ... @@ -1054,7 +1053,7 @@ if sys.version_info >= (3, 12): # It's writable on types, but not on instances of TypeAliasType. @property def __module__(self) -> str | None: ... # type: ignore[override] - def __getitem__(self, parameters: Any) -> Any: ... + def __getitem__(self, parameters: Any) -> GenericAlias: ... def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index a7d2b2c2e0835..a6b606e6b670f 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -58,6 +58,7 @@ from typing import ( # noqa: Y022,Y037,Y038,Y039 TextIO as TextIO, Tuple as Tuple, Type as Type, + TypedDict as TypedDict, Union as Union, ValuesView as ValuesView, _Alias, @@ -190,8 +191,10 @@ _T = typing.TypeVar("_T") _F = typing.TypeVar("_F", bound=Callable[..., Any]) _TC = typing.TypeVar("_TC", bound=type[object]) +class _Final: ... # This should be imported from typing but that breaks pytype + # unfortunately we have to duplicate this class definition from typing.pyi or we break pytype -class _SpecialForm: +class _SpecialForm(_Final): def __getitem__(self, parameters: Any) -> object: ... if sys.version_info >= (3, 10): def __or__(self, other: Any) -> _SpecialForm: ... @@ -253,15 +256,12 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): # supposedly incompatible definitions of `__ior__` and `__or__`: def __ior__(self, value: Self, /) -> Self: ... # type: ignore[misc] -# TypedDict is a (non-subscriptable) special form. -TypedDict: object - OrderedDict = _Alias() def get_type_hints( obj: Callable[..., Any], globalns: dict[str, Any] | None = None, - localns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, include_extras: bool = False, ) -> dict[str, Any]: ... def get_args(tp: Any) -> tuple[Any, ...]: ... @@ -403,13 +403,17 @@ else: # It's writable on types, but not on instances of TypeAliasType. @property def __module__(self) -> str | None: ... # type: ignore[override] + # Returns typing._GenericAlias, which isn't stubbed. def __getitem__(self, parameters: Any) -> Any: ... if sys.version_info >= (3, 10): def __or__(self, right: Any) -> _SpecialForm: ... def __ror__(self, left: Any) -> _SpecialForm: ... + # mypy and pyright object to this being both ABC and Protocol. + # At runtime it inherits from ABC and is not a Protocol, but it is on the + # allowlist for use as a Protocol. @runtime_checkable - class Buffer(Protocol): + class Buffer(Protocol, abc.ABC): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Not actually a Protocol at runtime; see # https://github.com/python/typeshed/issues/10224 for why we're defining it this way def __buffer__(self, flags: int, /) -> memoryview: ... diff --git a/mypy/typeshed/stdlib/unittest/__init__.pyi b/mypy/typeshed/stdlib/unittest/__init__.pyi index f2532ccf7fd8d..546ea77bb4ca2 100644 --- a/mypy/typeshed/stdlib/unittest/__init__.pyi +++ b/mypy/typeshed/stdlib/unittest/__init__.pyi @@ -11,13 +11,7 @@ from .case import ( skipIf as skipIf, skipUnless as skipUnless, ) -from .loader import ( - TestLoader as TestLoader, - defaultTestLoader as defaultTestLoader, - findTestCases as findTestCases, - getTestCaseNames as getTestCaseNames, - makeSuite as makeSuite, -) +from .loader import TestLoader as TestLoader, defaultTestLoader as defaultTestLoader from .main import TestProgram as TestProgram, main as main from .result import TestResult as TestResult from .runner import TextTestResult as TextTestResult, TextTestRunner as TextTestRunner @@ -52,12 +46,14 @@ __all__ = [ "registerResult", "removeResult", "removeHandler", - "getTestCaseNames", - "makeSuite", - "findTestCases", "addModuleCleanup", ] +if sys.version_info < (3, 13): + from .loader import findTestCases as findTestCases, getTestCaseNames as getTestCaseNames, makeSuite as makeSuite + + __all__ += ["getTestCaseNames", "makeSuite", "findTestCases"] + if sys.version_info >= (3, 11): __all__ += ["enterModuleContext", "doModuleCleanups"] diff --git a/mypy/typeshed/stdlib/unittest/async_case.pyi b/mypy/typeshed/stdlib/unittest/async_case.pyi index 12d6ef49e8282..565dd91c0fda8 100644 --- a/mypy/typeshed/stdlib/unittest/async_case.pyi +++ b/mypy/typeshed/stdlib/unittest/async_case.pyi @@ -1,4 +1,5 @@ import sys +from asyncio.events import AbstractEventLoop from collections.abc import Awaitable, Callable from typing import TypeVar from typing_extensions import ParamSpec @@ -12,6 +13,9 @@ _T = TypeVar("_T") _P = ParamSpec("_P") class IsolatedAsyncioTestCase(TestCase): + if sys.version_info >= (3, 13): + loop_factory: Callable[[], AbstractEventLoop] | None = None + async def asyncSetUp(self) -> None: ... async def asyncTearDown(self) -> None: ... def addAsyncCleanup(self, func: Callable[_P, Awaitable[object]], /, *args: _P.args, **kwargs: _P.kwargs) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index b63292604ecc5..a92f03f9745fe 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -6,7 +6,20 @@ from collections.abc import Callable, Container, Iterable, Mapping, Sequence, Se from contextlib import AbstractContextManager from re import Pattern from types import TracebackType -from typing import Any, AnyStr, ClassVar, Generic, NamedTuple, NoReturn, Protocol, SupportsAbs, SupportsRound, TypeVar, overload +from typing import ( + Any, + AnyStr, + ClassVar, + Final, + Generic, + NamedTuple, + NoReturn, + Protocol, + SupportsAbs, + SupportsRound, + TypeVar, + overload, +) from typing_extensions import ParamSpec, Self, TypeAlias from warnings import WarningMessage @@ -22,7 +35,7 @@ _E = TypeVar("_E", bound=BaseException) _FT = TypeVar("_FT", bound=Callable[..., Any]) _P = ParamSpec("_P") -DIFF_OMITTED: str +DIFF_OMITTED: Final[str] class _BaseTestCaseContext: test_case: TestCase diff --git a/mypy/typeshed/stdlib/unittest/loader.pyi b/mypy/typeshed/stdlib/unittest/loader.pyi index 202309ac1d930..598e3cd84a5e8 100644 --- a/mypy/typeshed/stdlib/unittest/loader.pyi +++ b/mypy/typeshed/stdlib/unittest/loader.pyi @@ -4,13 +4,13 @@ import unittest.suite from collections.abc import Callable, Sequence from re import Pattern from types import ModuleType -from typing import Any -from typing_extensions import TypeAlias +from typing import Any, Final +from typing_extensions import TypeAlias, deprecated _SortComparisonMethod: TypeAlias = Callable[[str, str], int] _SuiteClass: TypeAlias = Callable[[list[unittest.case.TestCase]], unittest.suite.TestSuite] -VALID_MODULE_NAME: Pattern[str] +VALID_MODULE_NAME: Final[Pattern[str]] class TestLoader: errors: list[type[BaseException]] @@ -34,18 +34,22 @@ class TestLoader: defaultTestLoader: TestLoader -def getTestCaseNames( - testCaseClass: type[unittest.case.TestCase], - prefix: str, - sortUsing: _SortComparisonMethod = ..., - testNamePatterns: list[str] | None = None, -) -> Sequence[str]: ... -def makeSuite( - testCaseClass: type[unittest.case.TestCase], - prefix: str = "test", - sortUsing: _SortComparisonMethod = ..., - suiteClass: _SuiteClass = ..., -) -> unittest.suite.TestSuite: ... -def findTestCases( - module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ... -) -> unittest.suite.TestSuite: ... +if sys.version_info < (3, 13): + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def getTestCaseNames( + testCaseClass: type[unittest.case.TestCase], + prefix: str, + sortUsing: _SortComparisonMethod = ..., + testNamePatterns: list[str] | None = None, + ) -> Sequence[str]: ... + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def makeSuite( + testCaseClass: type[unittest.case.TestCase], + prefix: str = "test", + sortUsing: _SortComparisonMethod = ..., + suiteClass: _SuiteClass = ..., + ) -> unittest.suite.TestSuite: ... + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def findTestCases( + module: ModuleType, prefix: str = "test", sortUsing: _SortComparisonMethod = ..., suiteClass: _SuiteClass = ... + ) -> unittest.suite.TestSuite: ... diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index 55bc1ec741db1..22f2ec10634d6 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -5,10 +5,11 @@ import unittest.result import unittest.suite from collections.abc import Iterable from types import ModuleType -from typing import Any, Protocol +from typing import Any, Final, Protocol +from typing_extensions import deprecated -MAIN_EXAMPLES: str -MODULE_EXAMPLES: str +MAIN_EXAMPLES: Final[str] +MODULE_EXAMPLES: Final[str] class _TestRunner(Protocol): def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase, /) -> unittest.result.TestResult: ... @@ -61,7 +62,10 @@ class TestProgram: tb_locals: bool = False, ) -> None: ... - def usageExit(self, msg: Any = None) -> None: ... + if sys.version_info < (3, 13): + @deprecated("Deprecated in Python 3.11; removal scheduled for Python 3.13") + def usageExit(self, msg: Any = None) -> None: ... + def parseArgs(self, argv: list[str]) -> None: ... def createTests(self, from_discovery: bool = False, Loader: unittest.loader.TestLoader | None = None) -> None: ... def runTests(self) -> None: ... # undocumented diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index dd61b83a658a2..193a4123c395a 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -12,23 +12,44 @@ _F = TypeVar("_F", bound=Callable[..., Any]) _AF = TypeVar("_AF", bound=Callable[..., Coroutine[Any, Any, Any]]) _P = ParamSpec("_P") -__all__ = ( - "Mock", - "MagicMock", - "patch", - "sentinel", - "DEFAULT", - "ANY", - "call", - "create_autospec", - "AsyncMock", - "FILTER_DIR", - "NonCallableMock", - "NonCallableMagicMock", - "mock_open", - "PropertyMock", - "seal", -) +if sys.version_info >= (3, 13): + # ThreadingMock added in 3.13 + __all__ = ( + "Mock", + "MagicMock", + "patch", + "sentinel", + "DEFAULT", + "ANY", + "call", + "create_autospec", + "ThreadingMock", + "AsyncMock", + "FILTER_DIR", + "NonCallableMock", + "NonCallableMagicMock", + "mock_open", + "PropertyMock", + "seal", + ) +else: + __all__ = ( + "Mock", + "MagicMock", + "patch", + "sentinel", + "DEFAULT", + "ANY", + "call", + "create_autospec", + "AsyncMock", + "FILTER_DIR", + "NonCallableMock", + "NonCallableMagicMock", + "mock_open", + "PropertyMock", + "seal", + ) if sys.version_info < (3, 9): __version__: Final[str] @@ -124,7 +145,6 @@ class NonCallableMock(Base, Any): def __delattr__(self, name: str) -> None: ... def __setattr__(self, name: str, value: Any) -> None: ... def __dir__(self) -> list[str]: ... - def _calls_repr(self, prefix: str = "Calls") -> str: ... def assert_called_with(self, *args: Any, **kwargs: Any) -> None: ... def assert_not_called(self) -> None: ... def assert_called_once_with(self, *args: Any, **kwargs: Any) -> None: ... @@ -150,6 +170,10 @@ class NonCallableMock(Base, Any): def _format_mock_call_signature(self, args: Any, kwargs: Any) -> str: ... def _call_matcher(self, _call: tuple[_Call, ...]) -> _Call: ... def _get_child_mock(self, **kw: Any) -> NonCallableMock: ... + if sys.version_info >= (3, 13): + def _calls_repr(self) -> str: ... + else: + def _calls_repr(self, prefix: str = "Calls") -> str: ... class CallableMixin(Base): side_effect: Any @@ -275,7 +299,7 @@ class _patcher: # Ideally we'd be able to add an overload for it so that the return type is _patch[MagicMock], # but that's impossible with the current type system. @overload - def __call__( # type: ignore[overload-overlap] + def __call__( self, target: str, new: _T, @@ -339,7 +363,7 @@ class _patcher: patch: _patcher -class MagicMixin: +class MagicMixin(Base): def __init__(self, *args: Any, **kw: Any) -> None: ... class NonCallableMagicMock(MagicMixin, NonCallableMock): ... @@ -369,7 +393,7 @@ class AsyncMock(AsyncMockMixin, AsyncMagicMixin, Mock): # But, `NonCallableMock` super-class has the better version. def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... -class MagicProxy: +class MagicProxy(Base): name: str parent: Any def __init__(self, name: str, parent: Any) -> None: ... @@ -427,4 +451,16 @@ class PropertyMock(Mock): def __get__(self, obj: _T, obj_type: type[_T] | None = None) -> Self: ... def __set__(self, obj: Any, val: Any) -> None: ... +if sys.version_info >= (3, 13): + class ThreadingMixin(Base): + DEFAULT_TIMEOUT: Final[float | None] = None + + def __init__(self, /, *args: Any, timeout: float | None | _SentinelObject = ..., **kwargs: Any) -> None: ... + # Same as `NonCallableMock.reset_mock.` + def reset_mock(self, visited: Any = None, *, return_value: bool = False, side_effect: bool = False) -> None: ... + def wait_until_called(self, *, timeout: float | None | _SentinelObject = ...) -> None: ... + def wait_until_any_call_with(self, *args: Any, **kwargs: Any) -> None: ... + + class ThreadingMock(ThreadingMixin, MagicMixin, Mock): ... + def seal(mock: Any) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/result.pyi b/mypy/typeshed/stdlib/unittest/result.pyi index 436fabf20c658..0761baaa2830b 100644 --- a/mypy/typeshed/stdlib/unittest/result.pyi +++ b/mypy/typeshed/stdlib/unittest/result.pyi @@ -2,14 +2,14 @@ import sys import unittest.case from _typeshed import OptExcInfo from collections.abc import Callable -from typing import Any, TextIO, TypeVar +from typing import Any, Final, TextIO, TypeVar from typing_extensions import TypeAlias _F = TypeVar("_F", bound=Callable[..., Any]) _DurationsType: TypeAlias = list[tuple[str, float]] -STDOUT_LINE: str -STDERR_LINE: str +STDOUT_LINE: Final[str] +STDERR_LINE: Final[str] # undocumented def failfast(method: _F) -> _F: ... diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index 0033083ac406b..393d03dfa0fcb 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -2,36 +2,56 @@ import sys import unittest.case import unittest.result import unittest.suite -from _typeshed import Incomplete +from _typeshed import SupportsFlush, SupportsWrite from collections.abc import Callable, Iterable -from typing import TextIO -from typing_extensions import TypeAlias +from typing import Any, Generic, Protocol, TypeVar +from typing_extensions import Never, TypeAlias -_ResultClassType: TypeAlias = Callable[[TextIO, bool, int], unittest.result.TestResult] +_ResultClassType: TypeAlias = Callable[[_TextTestStream, bool, int], TextTestResult] -class TextTestResult(unittest.result.TestResult): +class _SupportsWriteAndFlush(SupportsWrite[str], SupportsFlush, Protocol): ... + +# All methods used by unittest.runner.TextTestResult's stream +class _TextTestStream(_SupportsWriteAndFlush, Protocol): + def writeln(self, arg: str | None = None, /) -> str: ... + +# _WritelnDecorator should have all the same attrs as its stream param. +# But that's not feasible to do Generically +# We can expand the attributes if requested +class _WritelnDecorator: + def __init__(self, stream: _TextTestStream) -> None: ... + def writeln(self, arg: str | None = None) -> str: ... + def __getattr__(self, attr: str) -> Any: ... # Any attribute from the stream type passed to __init__ + # These attributes are prevented by __getattr__ + stream: Never + __getstate__: Never + # Methods proxied from the wrapped stream object via __getattr__ + def flush(self) -> object: ... + def write(self, s: str, /) -> object: ... + +_StreamT = TypeVar("_StreamT", bound=_TextTestStream, default=_WritelnDecorator) + +class TextTestResult(unittest.result.TestResult, Generic[_StreamT]): descriptions: bool # undocumented dots: bool # undocumented separator1: str separator2: str showAll: bool # undocumented - stream: TextIO # undocumented + stream: _StreamT # undocumented if sys.version_info >= (3, 12): durations: unittest.result._DurationsType | None def __init__( - self, stream: TextIO, descriptions: bool, verbosity: int, *, durations: unittest.result._DurationsType | None = None + self, stream: _StreamT, descriptions: bool, verbosity: int, *, durations: unittest.result._DurationsType | None = None ) -> None: ... else: - def __init__(self, stream: TextIO, descriptions: bool, verbosity: int) -> None: ... + def __init__(self, stream: _StreamT, descriptions: bool, verbosity: int) -> None: ... def getDescription(self, test: unittest.case.TestCase) -> str: ... def printErrorList(self, flavour: str, errors: Iterable[tuple[unittest.case.TestCase, str]]) -> None: ... class TextTestRunner: resultclass: _ResultClassType - # TODO: add `_WritelnDecorator` type - # stream: _WritelnDecorator - stream: Incomplete + stream: _WritelnDecorator descriptions: bool verbosity: int failfast: bool @@ -43,7 +63,7 @@ class TextTestRunner: durations: unittest.result._DurationsType | None def __init__( self, - stream: TextIO | None = None, + stream: _SupportsWriteAndFlush | None = None, descriptions: bool = True, verbosity: int = 1, failfast: bool = False, @@ -57,7 +77,7 @@ class TextTestRunner: else: def __init__( self, - stream: TextIO | None = None, + stream: _SupportsWriteAndFlush | None = None, descriptions: bool = True, verbosity: int = 1, failfast: bool = False, @@ -68,5 +88,5 @@ class TextTestRunner: tb_locals: bool = False, ) -> None: ... - def _makeResult(self) -> unittest.result.TestResult: ... - def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase) -> unittest.result.TestResult: ... + def _makeResult(self) -> TextTestResult: ... + def run(self, test: unittest.suite.TestSuite | unittest.case.TestCase) -> TextTestResult: ... diff --git a/mypy/typeshed/stdlib/unittest/suite.pyi b/mypy/typeshed/stdlib/unittest/suite.pyi index c10cbc75d7fd8..ff583d0766a03 100644 --- a/mypy/typeshed/stdlib/unittest/suite.pyi +++ b/mypy/typeshed/stdlib/unittest/suite.pyi @@ -5,7 +5,7 @@ from typing_extensions import TypeAlias _TestType: TypeAlias = unittest.case.TestCase | TestSuite -class BaseTestSuite(Iterable[_TestType]): +class BaseTestSuite: _tests: list[unittest.case.TestCase] _removed_tests: int def __init__(self, tests: Iterable[_TestType] = ()) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/util.pyi b/mypy/typeshed/stdlib/unittest/util.pyi index c42d1346e4b76..945b0cecfed09 100644 --- a/mypy/typeshed/stdlib/unittest/util.pyi +++ b/mypy/typeshed/stdlib/unittest/util.pyi @@ -1,16 +1,16 @@ from collections.abc import MutableSequence, Sequence -from typing import Any, TypeVar +from typing import Any, Final, TypeVar from typing_extensions import TypeAlias _T = TypeVar("_T") _Mismatch: TypeAlias = tuple[_T, _T, int] -_MAX_LENGTH: int -_PLACEHOLDER_LEN: int -_MIN_BEGIN_LEN: int -_MIN_END_LEN: int -_MIN_COMMON_LEN: int -_MIN_DIFF_LEN: int +_MAX_LENGTH: Final[int] +_PLACEHOLDER_LEN: Final[int] +_MIN_BEGIN_LEN: Final[int] +_MIN_END_LEN: Final[int] +_MIN_COMMON_LEN: Final[int] +_MIN_DIFF_LEN: Final[int] def _shorten(s: str, prefixlen: int, suffixlen: int) -> str: ... def _common_shorten_repr(*args: str) -> tuple[str, ...]: ... diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi index 89a50995d5530..785bb9678ec7f 100644 --- a/mypy/typeshed/stdlib/urllib/parse.pyi +++ b/mypy/typeshed/stdlib/urllib/parse.pyi @@ -198,13 +198,13 @@ else: # Requires an iterable of length 6 @overload -def urlunparse(components: Iterable[None]) -> Literal[b""]: ... +def urlunparse(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] @overload def urlunparse(components: Iterable[AnyStr | None]) -> AnyStr: ... # Requires an iterable of length 5 @overload -def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... +def urlunsplit(components: Iterable[None]) -> Literal[b""]: ... # type: ignore[overload-overlap] @overload def urlunsplit(components: Iterable[AnyStr | None]) -> AnyStr: ... def unwrap(url: str) -> str: ... diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index 2a6476f9e6d83..ad4f91fc31ae7 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -79,6 +79,7 @@ else: def pathname2url(pathname: str) -> str: ... def getproxies() -> dict[str, str]: ... +def getproxies_environment() -> dict[str, str]: ... def parse_http_list(s: str) -> list[str]: ... def parse_keqv_list(l: list[str]) -> dict[str, str]: ... diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index 539a8f2379c10..533a368175068 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -1,3 +1,4 @@ +import re import sys from _warnings import warn as warn, warn_explicit as warn_explicit from collections.abc import Sequence @@ -21,9 +22,11 @@ if sys.version_info >= (3, 13): _T = TypeVar("_T") _W = TypeVar("_W", bound=list[WarningMessage] | None) -_ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"] - -filters: Sequence[tuple[str, str | None, type[Warning], str | None, int]] # undocumented, do not mutate +if sys.version_info >= (3, 14): + _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"] +else: + _ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "all", "module", "once"] +filters: Sequence[tuple[str, re.Pattern[str] | None, type[Warning], re.Pattern[str] | None, int]] # undocumented, do not mutate def showwarning( message: Warning | str, diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi index 9137f1e476438..9319d5347c791 100644 --- a/mypy/typeshed/stdlib/wave.pyi +++ b/mypy/typeshed/stdlib/wave.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import ReadableBuffer, Unused -from typing import IO, Any, BinaryIO, Literal, NamedTuple, NoReturn, overload +from typing import IO, Any, BinaryIO, Final, Literal, NamedTuple, NoReturn, overload from typing_extensions import Self, TypeAlias, deprecated if sys.version_info >= (3, 9): @@ -12,7 +12,7 @@ _File: TypeAlias = str | IO[bytes] class Error(Exception): ... -WAVE_FORMAT_PCM: Literal[1] +WAVE_FORMAT_PCM: Final = 1 class _wave_params(NamedTuple): nchannels: int diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index aaba7ffc98d95..853caf3e8abb2 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -1,19 +1,14 @@ import sys from _typeshed import SupportsKeysAndGetItem -from _weakref import ( - CallableProxyType as CallableProxyType, - ProxyType as ProxyType, - ReferenceType as ReferenceType, - getweakrefcount as getweakrefcount, - getweakrefs as getweakrefs, - proxy as proxy, - ref as ref, -) +from _weakref import getweakrefcount as getweakrefcount, getweakrefs as getweakrefs, proxy as proxy from _weakrefset import WeakSet as WeakSet from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping -from typing import Any, Generic, TypeVar, overload +from typing import Any, Generic, TypeVar, final, overload from typing_extensions import ParamSpec, Self +if sys.version_info >= (3, 9): + from types import GenericAlias + __all__ = [ "ref", "proxy", @@ -40,11 +35,39 @@ _P = ParamSpec("_P") ProxyTypes: tuple[type[Any], ...] +# These classes are implemented in C and imported from _weakref at runtime. However, +# they consider themselves to live in the weakref module for sys.version_info >= (3, 11), +# so defining their stubs here means we match their __module__ value. +# Prior to 3.11 they did not declare a module for themselves and ended up looking like they +# came from the builtin module at runtime, which was just wrong, and we won't attempt to +# duplicate that. + +@final +class CallableProxyType(Generic[_CallableT]): # "weakcallableproxy" + def __eq__(self, value: object, /) -> bool: ... + def __getattr__(self, attr: str) -> Any: ... + __call__: _CallableT + +@final +class ProxyType(Generic[_T]): # "weakproxy" + def __eq__(self, value: object, /) -> bool: ... + def __getattr__(self, attr: str) -> Any: ... + +class ReferenceType(Generic[_T]): # "weakref" + __callback__: Callable[[Self], Any] + def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ... + def __call__(self) -> _T | None: ... + def __eq__(self, value: object, /) -> bool: ... + def __hash__(self) -> int: ... + if sys.version_info >= (3, 9): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + +ref = ReferenceType + +# everything below here is implemented in weakref.py + class WeakMethod(ref[_CallableT]): - # `ref` is implemented in `C` so positional-only arguments are enforced, but not in `WeakMethod`. - def __new__( # pyright: ignore[reportInconsistentConstructor] - cls, meth: _CallableT, callback: Callable[[Self], Any] | None = None - ) -> Self: ... + def __new__(cls, meth: _CallableT, callback: Callable[[Self], Any] | None = None) -> Self: ... def __call__(self) -> _CallableT | None: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... @@ -103,8 +126,8 @@ class WeakValueDictionary(MutableMapping[_KT, _VT]): class KeyedRef(ref[_T], Generic[_KT, _T]): key: _KT - def __new__(type, ob: _T, callback: Callable[[_T], Any], key: _KT) -> Self: ... - def __init__(self, ob: _T, callback: Callable[[_T], Any], key: _KT) -> None: ... + def __new__(type, ob: _T, callback: Callable[[Self], Any], key: _KT) -> Self: ... + def __init__(self, ob: _T, callback: Callable[[Self], Any], key: _KT) -> None: ... class WeakKeyDictionary(MutableMapping[_KT, _VT]): @overload diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi index 2b3f978c814bb..773786c248219 100644 --- a/mypy/typeshed/stdlib/webbrowser.pyi +++ b/mypy/typeshed/stdlib/webbrowser.pyi @@ -2,6 +2,7 @@ import sys from abc import abstractmethod from collections.abc import Callable, Sequence from typing import Literal +from typing_extensions import deprecated __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] @@ -62,8 +63,11 @@ if sys.platform == "win32": def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... if sys.platform == "darwin": - class MacOSX(BaseBrowser): - def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... + if sys.version_info < (3, 13): + @deprecated("Deprecated in 3.11, to be removed in 3.13.") + class MacOSX(BaseBrowser): + def __init__(self, name: str) -> None: ... + def open(self, url: str, new: int = 0, autoraise: bool = True) -> bool: ... class MacOSXOSAScript(BaseBrowser): # In runtime this class does not have `name` and `basename` if sys.version_info >= (3, 11): diff --git a/mypy/typeshed/stdlib/winsound.pyi b/mypy/typeshed/stdlib/winsound.pyi index bacc5302826f0..a20e81f94f98f 100644 --- a/mypy/typeshed/stdlib/winsound.pyi +++ b/mypy/typeshed/stdlib/winsound.pyi @@ -1,24 +1,24 @@ import sys from _typeshed import ReadableBuffer -from typing import Literal, overload +from typing import Final, Literal, overload if sys.platform == "win32": - SND_APPLICATION: Literal[128] - SND_FILENAME: Literal[131072] - SND_ALIAS: Literal[65536] - SND_LOOP: Literal[8] - SND_MEMORY: Literal[4] - SND_PURGE: Literal[64] - SND_ASYNC: Literal[1] - SND_NODEFAULT: Literal[2] - SND_NOSTOP: Literal[16] - SND_NOWAIT: Literal[8192] + SND_APPLICATION: Final = 128 + SND_FILENAME: Final = 131072 + SND_ALIAS: Final = 65536 + SND_LOOP: Final = 8 + SND_MEMORY: Final = 4 + SND_PURGE: Final = 64 + SND_ASYNC: Final = 1 + SND_NODEFAULT: Final = 2 + SND_NOSTOP: Final = 16 + SND_NOWAIT: Final = 8192 - MB_ICONASTERISK: Literal[64] - MB_ICONEXCLAMATION: Literal[48] - MB_ICONHAND: Literal[16] - MB_ICONQUESTION: Literal[32] - MB_OK: Literal[0] + MB_ICONASTERISK: Final = 64 + MB_ICONEXCLAMATION: Final = 48 + MB_ICONHAND: Final = 16 + MB_ICONQUESTION: Final = 32 + MB_OK: Final = 0 def Beep(frequency: int, duration: int) -> None: ... # Can actually accept anything ORed with 4, and if not it's definitely str, but that's inexpressible @overload diff --git a/mypy/typeshed/stdlib/wsgiref/types.pyi b/mypy/typeshed/stdlib/wsgiref/types.pyi index 86212df8ccdc4..57276fd05ea84 100644 --- a/mypy/typeshed/stdlib/wsgiref/types.pyi +++ b/mypy/typeshed/stdlib/wsgiref/types.pyi @@ -1,5 +1,5 @@ +from _typeshed import OptExcInfo from collections.abc import Callable, Iterable, Iterator -from sys import _OptExcInfo from typing import Any, Protocol from typing_extensions import TypeAlias @@ -7,7 +7,7 @@ __all__ = ["StartResponse", "WSGIEnvironment", "WSGIApplication", "InputStream", class StartResponse(Protocol): def __call__( - self, status: str, headers: list[tuple[str, str]], exc_info: _OptExcInfo | None = ..., / + self, status: str, headers: list[tuple[str, str]], exc_info: OptExcInfo | None = ..., / ) -> Callable[[bytes], object]: ... WSGIEnvironment: TypeAlias = dict[str, Any] diff --git a/mypy/typeshed/stdlib/xml/__init__.pyi b/mypy/typeshed/stdlib/xml/__init__.pyi index a487d2467f41b..7a240965136e5 100644 --- a/mypy/typeshed/stdlib/xml/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/__init__.pyi @@ -1 +1,3 @@ -from xml import parsers as parsers +# At runtime, listing submodules in __all__ without them being imported is +# valid, and causes them to be included in a star import. See #6523 +__all__ = ["dom", "parsers", "sax", "etree"] # noqa: F822 # pyright: ignore[reportUnsupportedDunderAll] diff --git a/mypy/typeshed/stdlib/xml/dom/__init__.pyi b/mypy/typeshed/stdlib/xml/dom/__init__.pyi index e5b91bf2a7950..8738015638a93 100644 --- a/mypy/typeshed/stdlib/xml/dom/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/dom/__init__.pyi @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Final from .domreg import getDOMImplementation as getDOMImplementation, registerDOMImplementation as registerDOMImplementation @@ -17,22 +17,22 @@ class Node: NOTATION_NODE: int # ExceptionCode -INDEX_SIZE_ERR: int -DOMSTRING_SIZE_ERR: int -HIERARCHY_REQUEST_ERR: int -WRONG_DOCUMENT_ERR: int -INVALID_CHARACTER_ERR: int -NO_DATA_ALLOWED_ERR: int -NO_MODIFICATION_ALLOWED_ERR: int -NOT_FOUND_ERR: int -NOT_SUPPORTED_ERR: int -INUSE_ATTRIBUTE_ERR: int -INVALID_STATE_ERR: int -SYNTAX_ERR: int -INVALID_MODIFICATION_ERR: int -NAMESPACE_ERR: int -INVALID_ACCESS_ERR: int -VALIDATION_ERR: int +INDEX_SIZE_ERR: Final[int] +DOMSTRING_SIZE_ERR: Final[int] +HIERARCHY_REQUEST_ERR: Final[int] +WRONG_DOCUMENT_ERR: Final[int] +INVALID_CHARACTER_ERR: Final[int] +NO_DATA_ALLOWED_ERR: Final[int] +NO_MODIFICATION_ALLOWED_ERR: Final[int] +NOT_FOUND_ERR: Final[int] +NOT_SUPPORTED_ERR: Final[int] +INUSE_ATTRIBUTE_ERR: Final[int] +INVALID_STATE_ERR: Final[int] +SYNTAX_ERR: Final[int] +INVALID_MODIFICATION_ERR: Final[int] +NAMESPACE_ERR: Final[int] +INVALID_ACCESS_ERR: Final[int] +VALIDATION_ERR: Final[int] class DOMException(Exception): code: int @@ -62,8 +62,8 @@ class UserDataHandler: NODE_DELETED: int NODE_RENAMED: int -XML_NAMESPACE: str -XMLNS_NAMESPACE: str -XHTML_NAMESPACE: str -EMPTY_NAMESPACE: None -EMPTY_PREFIX: None +XML_NAMESPACE: Final[str] +XMLNS_NAMESPACE: Final[str] +XHTML_NAMESPACE: Final[str] +EMPTY_NAMESPACE: Final[None] +EMPTY_PREFIX: Final[None] diff --git a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi index 95436ab5dd381..50250de5cb2f6 100644 --- a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi @@ -1,20 +1,20 @@ import sys from _typeshed import Incomplete, SupportsRead from collections.abc import Sequence -from typing import Literal +from typing import Final, Literal from typing_extensions import TypeAlias from xml.dom.minidom import Document, DOMImplementation, Element, Text from xml.sax.handler import ContentHandler from xml.sax.xmlreader import XMLReader -START_ELEMENT: Literal["START_ELEMENT"] -END_ELEMENT: Literal["END_ELEMENT"] -COMMENT: Literal["COMMENT"] -START_DOCUMENT: Literal["START_DOCUMENT"] -END_DOCUMENT: Literal["END_DOCUMENT"] -PROCESSING_INSTRUCTION: Literal["PROCESSING_INSTRUCTION"] -IGNORABLE_WHITESPACE: Literal["IGNORABLE_WHITESPACE"] -CHARACTERS: Literal["CHARACTERS"] +START_ELEMENT: Final = "START_ELEMENT" +END_ELEMENT: Final = "END_ELEMENT" +COMMENT: Final = "COMMENT" +START_DOCUMENT: Final = "START_DOCUMENT" +END_DOCUMENT: Final = "END_DOCUMENT" +PROCESSING_INSTRUCTION: Final = "PROCESSING_INSTRUCTION" +IGNORABLE_WHITESPACE: Final = "IGNORABLE_WHITESPACE" +CHARACTERS: Final = "CHARACTERS" _DocumentFactory: TypeAlias = DOMImplementation | None _Node: TypeAlias = Document | Element | Text diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi index cbba15dd3ebea..5a15772ec2a96 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi @@ -1,14 +1,15 @@ import sys from _typeshed import FileDescriptorOrPath from collections.abc import Callable +from typing import Final from xml.etree.ElementTree import Element -XINCLUDE: str -XINCLUDE_INCLUDE: str -XINCLUDE_FALLBACK: str +XINCLUDE: Final[str] +XINCLUDE_INCLUDE: Final[str] +XINCLUDE_FALLBACK: Final[str] if sys.version_info >= (3, 9): - DEFAULT_MAX_INCLUSION_DEPTH: int + DEFAULT_MAX_INCLUSION_DEPTH: Final = 6 class FatalIncludeError(SyntaxError): ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 9198bd3322d90..64ebbd3ee63f2 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -2,7 +2,7 @@ import sys from _collections_abc import dict_keys from _typeshed import FileDescriptorOrPath, ReadableBuffer, SupportsRead, SupportsWrite from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence -from typing import Any, Literal, SupportsIndex, TypeVar, overload +from typing import Any, Final, Literal, SupportsIndex, TypeVar, overload from typing_extensions import TypeAlias, TypeGuard, deprecated __all__ = [ @@ -41,7 +41,7 @@ _FileRead: TypeAlias = FileDescriptorOrPath | SupportsRead[bytes] | SupportsRead _FileWriteC14N: TypeAlias = FileDescriptorOrPath | SupportsWrite[bytes] _FileWrite: TypeAlias = _FileWriteC14N | SupportsWrite[str] -VERSION: str +VERSION: Final[str] class ParseError(SyntaxError): code: int @@ -239,9 +239,15 @@ if sys.version_info >= (3, 9): def indent(tree: Element | ElementTree, space: str = " ", level: int = 0) -> None: ... def parse(source: _FileRead, parser: XMLParser | None = None) -> ElementTree: ... -def iterparse( - source: _FileRead, events: Sequence[str] | None = None, parser: XMLParser | None = None -) -> Iterator[tuple[str, Any]]: ... + +class _IterParseIterator(Iterator[tuple[str, Any]]): + def __next__(self) -> tuple[str, Any]: ... + if sys.version_info >= (3, 13): + def close(self) -> None: ... + if sys.version_info >= (3, 11): + def __del__(self) -> None: ... + +def iterparse(source: _FileRead, events: Sequence[str] | None = None, parser: XMLParser | None = None) -> _IterParseIterator: ... class XMLPullParser: def __init__(self, events: Sequence[str] | None = None, *, _parser: XMLParser | None = None) -> None: ... diff --git a/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi b/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi index 73f3758c61ec1..d9b7ea5369998 100644 --- a/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/parsers/expat/__init__.pyi @@ -1 +1,7 @@ from pyexpat import * + +# This is actually implemented in the C module pyexpat, but considers itself to live here. +class ExpatError(Exception): + code: int + lineno: int + offset: int diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi index 2be5f7df2d7de..5899d1d72a389 100644 --- a/mypy/typeshed/stdlib/xmlrpc/client.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi @@ -6,7 +6,7 @@ from collections.abc import Callable, Iterable, Mapping from datetime import datetime from io import BytesIO from types import TracebackType -from typing import Any, Literal, Protocol, overload +from typing import Any, Final, Literal, Protocol, overload from typing_extensions import Self, TypeAlias class _SupportsTimeTuple(Protocol): @@ -34,22 +34,22 @@ _HostType: TypeAlias = tuple[str, dict[str, str]] | str def escape(s: str) -> str: ... # undocumented -MAXINT: int # undocumented -MININT: int # undocumented +MAXINT: Final[int] # undocumented +MININT: Final[int] # undocumented -PARSE_ERROR: int # undocumented -SERVER_ERROR: int # undocumented -APPLICATION_ERROR: int # undocumented -SYSTEM_ERROR: int # undocumented -TRANSPORT_ERROR: int # undocumented +PARSE_ERROR: Final[int] # undocumented +SERVER_ERROR: Final[int] # undocumented +APPLICATION_ERROR: Final[int] # undocumented +SYSTEM_ERROR: Final[int] # undocumented +TRANSPORT_ERROR: Final[int] # undocumented -NOT_WELLFORMED_ERROR: int # undocumented -UNSUPPORTED_ENCODING: int # undocumented -INVALID_ENCODING_CHAR: int # undocumented -INVALID_XMLRPC: int # undocumented -METHOD_NOT_FOUND: int # undocumented -INVALID_METHOD_PARAMS: int # undocumented -INTERNAL_ERROR: int # undocumented +NOT_WELLFORMED_ERROR: Final[int] # undocumented +UNSUPPORTED_ENCODING: Final[int] # undocumented +INVALID_ENCODING_CHAR: Final[int] # undocumented +INVALID_XMLRPC: Final[int] # undocumented +METHOD_NOT_FOUND: Final[int] # undocumented +INVALID_METHOD_PARAMS: Final[int] # undocumented +INTERNAL_ERROR: Final[int] # undocumented class Error(Exception): ... @@ -98,7 +98,7 @@ class Binary: def _binary(data: ReadableBuffer) -> Binary: ... # undocumented -WRAPPERS: tuple[type[DateTime], type[Binary]] # undocumented +WRAPPERS: Final[tuple[type[DateTime], type[Binary]]] # undocumented class ExpatParser: # undocumented def __init__(self, target: Unmarshaller) -> None: ... @@ -200,7 +200,7 @@ def dumps( allow_none: bool = False, ) -> str: ... def loads( - data: str, use_datetime: bool = False, use_builtin_types: bool = False + data: str | ReadableBuffer, use_datetime: bool = False, use_builtin_types: bool = False ) -> tuple[tuple[_Marshallable, ...], str | None]: ... def gzip_encode(data: ReadableBuffer) -> bytes: ... # undocumented def gzip_decode(data: ReadableBuffer, max_decode: int = 20971520) -> bytes: ... # undocumented diff --git a/mypy/typeshed/stdlib/xmlrpc/server.pyi b/mypy/typeshed/stdlib/xmlrpc/server.pyi index 8ca3a4d1a33c9..5f497aa7190e9 100644 --- a/mypy/typeshed/stdlib/xmlrpc/server.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/server.pyi @@ -1,6 +1,7 @@ import http.server import pydoc import socketserver +from _typeshed import ReadableBuffer from collections.abc import Callable, Iterable, Mapping from re import Pattern from typing import Any, ClassVar, Protocol @@ -48,8 +49,8 @@ class SimpleXMLRPCDispatcher: # undocumented def register_multicall_functions(self) -> None: ... def _marshaled_dispatch( self, - data: str, - dispatch_method: Callable[[str | None, tuple[_Marshallable, ...]], Fault | tuple[_Marshallable, ...]] | None = None, + data: str | ReadableBuffer, + dispatch_method: Callable[[str, tuple[_Marshallable, ...]], Fault | tuple[_Marshallable, ...]] | None = None, path: Any | None = None, ) -> str: ... # undocumented def system_listMethods(self) -> list[str]: ... # undocumented diff --git a/mypy/typeshed/stdlib/zipfile/__init__.pyi b/mypy/typeshed/stdlib/zipfile/__init__.pyi index b61e07f8b90d2..5b8f02f61bce6 100644 --- a/mypy/typeshed/stdlib/zipfile/__init__.pyi +++ b/mypy/typeshed/stdlib/zipfile/__init__.pyi @@ -5,7 +5,7 @@ from collections.abc import Callable, Iterable, Iterator from io import TextIOWrapper from os import PathLike from types import TracebackType -from typing import IO, Literal, Protocol, overload +from typing import IO, Final, Literal, Protocol, overload from typing_extensions import Self, TypeAlias __all__ = [ @@ -94,6 +94,20 @@ class ZipExtFile(io.BufferedIOBase): class _Writer(Protocol): def write(self, s: str, /) -> object: ... +class _ZipReadable(Protocol): + def seek(self, offset: int, whence: int = 0, /) -> int: ... + def read(self, n: int = -1, /) -> bytes: ... + +class _ZipTellable(Protocol): + def tell(self) -> int: ... + +class _ZipReadableTellable(_ZipReadable, _ZipTellable, Protocol): ... + +class _ZipWritable(Protocol): + def flush(self) -> None: ... + def close(self) -> None: ... + def write(self, b: bytes, /) -> int: ... + class ZipFile: filename: str | None debug: int @@ -106,24 +120,50 @@ class ZipFile: compresslevel: int | None # undocumented mode: _ZipFileMode # undocumented pwd: bytes | None # undocumented + # metadata_encoding is new in 3.11 if sys.version_info >= (3, 11): @overload def __init__( self, file: StrPath | IO[bytes], + mode: _ZipFileMode = "r", + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + metadata_encoding: str | None = None, + ) -> None: ... + # metadata_encoding is only allowed for read mode + @overload + def __init__( + self, + file: StrPath | _ZipReadable, mode: Literal["r"] = "r", compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, *, strict_timestamps: bool = True, - metadata_encoding: str | None, + metadata_encoding: str | None = None, ) -> None: ... @overload def __init__( self, - file: StrPath | IO[bytes], - mode: _ZipFileMode = "r", + file: StrPath | _ZipWritable, + mode: Literal["w", "x"] = ..., + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + metadata_encoding: None = None, + ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipReadableTellable, + mode: Literal["a"] = ..., compression: int = 0, allowZip64: bool = True, compresslevel: int | None = None, @@ -132,6 +172,7 @@ class ZipFile: metadata_encoding: None = None, ) -> None: ... else: + @overload def __init__( self, file: StrPath | IO[bytes], @@ -142,6 +183,39 @@ class ZipFile: *, strict_timestamps: bool = True, ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipReadable, + mode: Literal["r"] = "r", + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipWritable, + mode: Literal["w", "x"] = ..., + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + ) -> None: ... + @overload + def __init__( + self, + file: StrPath | _ZipReadableTellable, + mode: Literal["a"] = ..., + compression: int = 0, + allowZip64: bool = True, + compresslevel: int | None = None, + *, + strict_timestamps: bool = True, + ) -> None: ... def __enter__(self) -> Self: ... def __exit__( @@ -206,6 +280,9 @@ class ZipInfo: compress_size: int file_size: int orig_filename: str # undocumented + if sys.version_info >= (3, 13): + compress_level: int | None + def __init__(self, filename: str = "NoName", date_time: _DateTuple = (1980, 1, 1, 0, 0, 0)) -> None: ... @classmethod def from_file(cls, filename: StrPath, arcname: StrPath | None = None, *, strict_timestamps: bool = True) -> Self: ... @@ -227,6 +304,7 @@ else: class Path: root: CompleteDirs + at: str def __init__(self, root: ZipFile | StrPath | IO[bytes], at: str = "") -> None: ... @property def name(self) -> str: ... @@ -297,10 +375,10 @@ else: def is_zipfile(filename: StrOrBytesPath | _SupportsReadSeekTell) -> bool: ... -ZIP_STORED: int -ZIP_DEFLATED: int -ZIP64_LIMIT: int -ZIP_FILECOUNT_LIMIT: int -ZIP_MAX_COMMENT: int -ZIP_BZIP2: int -ZIP_LZMA: int +ZIP_STORED: Final[int] +ZIP_DEFLATED: Final[int] +ZIP64_LIMIT: Final[int] +ZIP_FILECOUNT_LIMIT: Final[int] +ZIP_MAX_COMMENT: Final[int] +ZIP_BZIP2: Final[int] +ZIP_LZMA: Final[int] diff --git a/mypy/typeshed/stdlib/zipfile/_path.pyi b/mypy/typeshed/stdlib/zipfile/_path.pyi index 0398824e1fd22..a7248ba7ab72f 100644 --- a/mypy/typeshed/stdlib/zipfile/_path.pyi +++ b/mypy/typeshed/stdlib/zipfile/_path.pyi @@ -3,13 +3,17 @@ from _typeshed import StrPath from collections.abc import Iterator, Sequence from io import TextIOWrapper from os import PathLike -from typing import IO, Literal, overload +from typing import IO, Literal, TypeVar, overload from typing_extensions import Self, TypeAlias from zipfile import ZipFile _ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] +_ZF = TypeVar("_ZF", bound=ZipFile) + if sys.version_info >= (3, 12): + __all__ = ["Path"] + class InitializedState: def __init__(self, *args: object, **kwargs: object) -> None: ... def __getstate__(self) -> tuple[list[object], dict[object, object]]: ... @@ -23,9 +27,13 @@ if sys.version_info >= (3, 12): @overload @classmethod def make(cls, source: StrPath | IO[bytes]) -> Self: ... + if sys.version_info >= (3, 13): + @classmethod + def inject(cls, zf: _ZF) -> _ZF: ... class Path: root: CompleteDirs + at: str def __init__(self, root: ZipFile | StrPath | IO[bytes], at: str = "") -> None: ... @property def name(self) -> str: ... diff --git a/mypy/typeshed/stdlib/zipimport.pyi b/mypy/typeshed/stdlib/zipimport.pyi index 158d573cac743..3e94c681b7a2b 100644 --- a/mypy/typeshed/stdlib/zipimport.pyi +++ b/mypy/typeshed/stdlib/zipimport.pyi @@ -3,12 +3,18 @@ from _typeshed import StrOrBytesPath from importlib.abc import ResourceReader from importlib.machinery import ModuleSpec from types import CodeType, ModuleType +from typing_extensions import deprecated + +if sys.version_info >= (3, 10): + from _frozen_importlib_external import _LoaderBasics +else: + _LoaderBasics = object __all__ = ["ZipImportError", "zipimporter"] class ZipImportError(ImportError): ... -class zipimporter: +class zipimporter(_LoaderBasics): archive: str prefix: str if sys.version_info >= (3, 11): @@ -26,7 +32,10 @@ class zipimporter: def get_resource_reader(self, fullname: str) -> ResourceReader | None: ... # undocumented def get_source(self, fullname: str) -> str | None: ... def is_package(self, fullname: str) -> bool: ... + @deprecated("Deprecated since 3.10; use exec_module() instead") def load_module(self, fullname: str) -> ModuleType: ... if sys.version_info >= (3, 10): + def exec_module(self, module: ModuleType) -> None: ... + def create_module(self, spec: ModuleSpec) -> None: ... def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: ... def invalidate_caches(self) -> None: ... diff --git a/mypy/typeshed/stdlib/zlib.pyi b/mypy/typeshed/stdlib/zlib.pyi index 234770172d40e..7cafb44b34a7b 100644 --- a/mypy/typeshed/stdlib/zlib.pyi +++ b/mypy/typeshed/stdlib/zlib.pyi @@ -1,43 +1,57 @@ import sys from _typeshed import ReadableBuffer -from typing import Literal +from typing import Any, Final, final, type_check_only +from typing_extensions import Self -DEFLATED: Literal[8] +DEFLATED: Final = 8 DEF_MEM_LEVEL: int # can change -DEF_BUF_SIZE: Literal[16384] +DEF_BUF_SIZE: Final = 16384 MAX_WBITS: int ZLIB_VERSION: str # can change ZLIB_RUNTIME_VERSION: str # can change -Z_NO_COMPRESSION: Literal[0] -Z_PARTIAL_FLUSH: Literal[1] -Z_BEST_COMPRESSION: Literal[9] -Z_BEST_SPEED: Literal[1] -Z_BLOCK: Literal[5] -Z_DEFAULT_COMPRESSION: Literal[-1] -Z_DEFAULT_STRATEGY: Literal[0] -Z_FILTERED: Literal[1] -Z_FINISH: Literal[4] -Z_FIXED: Literal[4] -Z_FULL_FLUSH: Literal[3] -Z_HUFFMAN_ONLY: Literal[2] -Z_NO_FLUSH: Literal[0] -Z_RLE: Literal[3] -Z_SYNC_FLUSH: Literal[2] -Z_TREES: Literal[6] +Z_NO_COMPRESSION: Final = 0 +Z_PARTIAL_FLUSH: Final = 1 +Z_BEST_COMPRESSION: Final = 9 +Z_BEST_SPEED: Final = 1 +Z_BLOCK: Final = 5 +Z_DEFAULT_COMPRESSION: Final = -1 +Z_DEFAULT_STRATEGY: Final = 0 +Z_FILTERED: Final = 1 +Z_FINISH: Final = 4 +Z_FIXED: Final = 4 +Z_FULL_FLUSH: Final = 3 +Z_HUFFMAN_ONLY: Final = 2 +Z_NO_FLUSH: Final = 0 +Z_RLE: Final = 3 +Z_SYNC_FLUSH: Final = 2 +Z_TREES: Final = 6 class error(Exception): ... +# This class is not exposed at runtime. It calls itself zlib.Compress. +@final +@type_check_only class _Compress: - def compress(self, data: ReadableBuffer) -> bytes: ... - def flush(self, mode: int = ...) -> bytes: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def compress(self, data: ReadableBuffer, /) -> bytes: ... + def flush(self, mode: int = 4, /) -> bytes: ... def copy(self) -> _Compress: ... +# This class is not exposed at runtime. It calls itself zlib.Decompress. +@final +@type_check_only class _Decompress: - unused_data: bytes - unconsumed_tail: bytes - eof: bool - def decompress(self, data: ReadableBuffer, max_length: int = ...) -> bytes: ... - def flush(self, length: int = ...) -> bytes: ... + @property + def unused_data(self) -> bytes: ... + @property + def unconsumed_tail(self) -> bytes: ... + @property + def eof(self) -> bool: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def decompress(self, data: ReadableBuffer, /, max_length: int = 0) -> bytes: ... + def flush(self, length: int = 16384, /) -> bytes: ... def copy(self) -> _Decompress: ... def adler32(data: ReadableBuffer, value: int = 1, /) -> int: ... diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi index 77930ac79dd55..cc483afad9fff 100644 --- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi +++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi @@ -1,38 +1,35 @@ -from _typeshed import StrPath -from collections.abc import Iterable, Sequence +import sys +from collections.abc import Iterable from datetime import datetime, timedelta, tzinfo -from typing import Any, Protocol from typing_extensions import Self -__all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"] +# TODO: remove this version check +# In theory we shouldn't need this version check. Pyright complains about the imports +# from zoneinfo.* when run on 3.8 and 3.7 without this. Updates to typeshed's +# pyright test script are probably needed, see #11189 +if sys.version_info >= (3, 9): + from zoneinfo._common import ZoneInfoNotFoundError as ZoneInfoNotFoundError, _IOBytes + from zoneinfo._tzpath import ( + TZPATH as TZPATH, + InvalidTZPathWarning as InvalidTZPathWarning, + available_timezones as available_timezones, + reset_tzpath as reset_tzpath, + ) -class _IOBytes(Protocol): - def read(self, size: int, /) -> bytes: ... - def seek(self, size: int, whence: int = ..., /) -> Any: ... + __all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"] -class ZoneInfo(tzinfo): - @property - def key(self) -> str: ... - def __init__(self, key: str) -> None: ... - @classmethod - def no_cache(cls, key: str) -> Self: ... - @classmethod - def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ... - @classmethod - def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ... - def tzname(self, dt: datetime | None, /) -> str | None: ... - def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ... - def dst(self, dt: datetime | None, /) -> timedelta | None: ... + class ZoneInfo(tzinfo): + @property + def key(self) -> str: ... + def __init__(self, key: str) -> None: ... + @classmethod + def no_cache(cls, key: str) -> Self: ... + @classmethod + def from_file(cls, fobj: _IOBytes, /, key: str | None = None) -> Self: ... + @classmethod + def clear_cache(cls, *, only_keys: Iterable[str] | None = None) -> None: ... + def tzname(self, dt: datetime | None, /) -> str | None: ... + def utcoffset(self, dt: datetime | None, /) -> timedelta | None: ... + def dst(self, dt: datetime | None, /) -> timedelta | None: ... -# Note: Both here and in clear_cache, the types allow the use of `str` where -# a sequence of strings is required. This should be remedied if a solution -# to this typing bug is found: https://github.com/python/typing/issues/256 -def reset_tzpath(to: Sequence[StrPath] | None = None) -> None: ... -def available_timezones() -> set[str]: ... - -TZPATH: tuple[str, ...] - -class ZoneInfoNotFoundError(KeyError): ... -class InvalidTZPathWarning(RuntimeWarning): ... - -def __dir__() -> list[str]: ... + def __dir__() -> list[str]: ... diff --git a/mypy/typeshed/stdlib/zoneinfo/_common.pyi b/mypy/typeshed/stdlib/zoneinfo/_common.pyi new file mode 100644 index 0000000000000..a2f29f2d14f08 --- /dev/null +++ b/mypy/typeshed/stdlib/zoneinfo/_common.pyi @@ -0,0 +1,13 @@ +import io +from typing import Any, Protocol + +class _IOBytes(Protocol): + def read(self, size: int, /) -> bytes: ... + def seek(self, size: int, whence: int = ..., /) -> Any: ... + +def load_tzdata(key: str) -> io.BufferedReader: ... +def load_data( + fobj: _IOBytes, +) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...], tuple[int, ...], tuple[str, ...], bytes | None]: ... + +class ZoneInfoNotFoundError(KeyError): ... diff --git a/mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi b/mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi new file mode 100644 index 0000000000000..0ef78d03e5f4a --- /dev/null +++ b/mypy/typeshed/stdlib/zoneinfo/_tzpath.pyi @@ -0,0 +1,13 @@ +from _typeshed import StrPath +from collections.abc import Sequence + +# Note: Both here and in clear_cache, the types allow the use of `str` where +# a sequence of strings is required. This should be remedied if a solution +# to this typing bug is found: https://github.com/python/typing/issues/256 +def reset_tzpath(to: Sequence[StrPath] | None = None) -> None: ... +def find_tzfile(key: str) -> str | None: ... +def available_timezones() -> set[str]: ... + +TZPATH: tuple[str, ...] + +class InvalidTZPathWarning(RuntimeWarning): ... diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index 4d740a802b558..a28bbf422b612 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -130,8 +130,7 @@ def visit_partial_type(self, t: PartialType) -> None: pass def visit_raw_expression_type(self, t: RawExpressionType) -> None: - if t.node is not None: - t.node.accept(self) + pass def visit_type_alias_type(self, t: TypeAliasType) -> None: # TODO: sometimes we want to traverse target as well diff --git a/mypy/typevars.py b/mypy/typevars.py index 3d74a40c303f7..e871973104a2d 100644 --- a/mypy/typevars.py +++ b/mypy/typevars.py @@ -3,7 +3,6 @@ from mypy.erasetype import erase_typevars from mypy.nodes import TypeInfo from mypy.types import ( - AnyType, Instance, ParamSpecType, ProperType, @@ -15,6 +14,7 @@ TypeVarType, UnpackType, ) +from mypy.typevartuples import erased_vars def fill_typevars(typ: TypeInfo) -> Instance | TupleType: @@ -64,16 +64,7 @@ def fill_typevars(typ: TypeInfo) -> Instance | TupleType: def fill_typevars_with_any(typ: TypeInfo) -> Instance | TupleType: """Apply a correct number of Any's as type arguments to a type.""" - args: list[Type] = [] - for tv in typ.defn.type_vars: - # Valid erasure for *Ts is *tuple[Any, ...], not just Any. - if isinstance(tv, TypeVarTupleType): - args.append( - UnpackType(tv.tuple_fallback.copy_modified(args=[AnyType(TypeOfAny.special_form)])) - ) - else: - args.append(AnyType(TypeOfAny.special_form)) - inst = Instance(typ, args) + inst = Instance(typ, erased_vars(typ.defn.type_vars, TypeOfAny.special_form)) if typ.tuple_type is None: return inst erased_tuple_type = erase_typevars(typ.tuple_type, {tv.id for tv in typ.defn.type_vars}) diff --git a/mypy/typevartuples.py b/mypy/typevartuples.py index af2effbd4035f..3bc67dc55ef3b 100644 --- a/mypy/typevartuples.py +++ b/mypy/typevartuples.py @@ -5,11 +5,12 @@ from typing import Sequence from mypy.types import ( + AnyType, Instance, - ProperType, Type, + TypeVarLikeType, + TypeVarTupleType, UnpackType, - get_proper_type, split_with_prefix_and_suffix, ) @@ -24,9 +25,12 @@ def split_with_instance( ) -def extract_unpack(types: Sequence[Type]) -> ProperType | None: - """Given a list of types, extracts either a single type from an unpack, or returns None.""" - if len(types) == 1: - if isinstance(types[0], UnpackType): - return get_proper_type(types[0].type) - return None +def erased_vars(type_vars: Sequence[TypeVarLikeType], type_of_any: int) -> list[Type]: + args: list[Type] = [] + for tv in type_vars: + # Valid erasure for *Ts is *tuple[Any, ...], not just Any. + if isinstance(tv, TypeVarTupleType): + args.append(UnpackType(tv.tuple_fallback.copy_modified(args=[AnyType(type_of_any)]))) + else: + args.append(AnyType(type_of_any)) + return args diff --git a/mypy/util.py b/mypy/util.py index 4b1b918b92e6b..e0a9cf9ce1b25 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -4,20 +4,25 @@ import hashlib import io +import json import os -import pathlib import re import shutil import sys import time from importlib import resources as importlib_resources -from typing import IO, Callable, Container, Final, Iterable, Sequence, Sized, TypeVar +from typing import IO, Any, Callable, Container, Final, Iterable, Sequence, Sized, TypeVar from typing_extensions import Literal +orjson: Any try: - import curses + import orjson # type: ignore[import-not-found, no-redef, unused-ignore] +except ImportError: + orjson = None +try: import _curses # noqa: F401 + import curses CURSES_ENABLED = True except ImportError: @@ -196,7 +201,7 @@ def trim_source_line(line: str, max_len: int, col: int, min_width: int) -> tuple A typical result looks like this: ...some_variable = function_to_call(one_arg, other_arg) or... - Return the trimmed string and the column offset to to adjust error location. + Return the trimmed string and the column offset to adjust error location. """ if max_len < 2 * min_width + 1: # In case the window is too tiny it is better to still show something. @@ -412,9 +417,43 @@ def replace_object_state( pass -def is_sub_path(path1: str, path2: str) -> bool: - """Given two paths, return if path1 is a sub-path of path2.""" - return pathlib.Path(path2) in pathlib.Path(path1).parents +def is_sub_path_normabs(path: str, dir: str) -> bool: + """Given two paths, return if path is a sub-path of dir. + + Moral equivalent of: Path(dir) in Path(path).parents + + Similar to the pathlib version: + - Treats paths case-sensitively + - Does not fully handle unnormalised paths (e.g. paths with "..") + - Does not handle a mix of absolute and relative paths + Unlike the pathlib version: + - Fast + - On Windows, assumes input has been slash normalised + - Handles even fewer unnormalised paths (e.g. paths with "." and "//") + + As a result, callers should ensure that inputs have had os.path.abspath called on them + (note that os.path.abspath will normalise) + """ + if not dir.endswith(os.sep): + dir += os.sep + return path.startswith(dir) + + +if sys.platform == "linux" or sys.platform == "darwin": + + def os_path_join(path: str, b: str) -> str: + # Based off of os.path.join, but simplified to str-only, 2 args and mypyc can compile it. + if b.startswith("/") or not path: + return b + elif path.endswith("/"): + return path + b + else: + return path + "/" + b + +else: + + def os_path_join(a: str, p: str) -> str: + return os.path.join(a, p) def hard_exit(status: int = 0) -> None: @@ -535,9 +574,7 @@ def hash_digest(data: bytes) -> str: accidental collision, but we don't really care about any of the cryptographic properties. """ - # Once we drop Python 3.5 support, we should consider using - # blake2b, which is faster. - return hashlib.sha256(data).hexdigest() + return hashlib.sha1(data).hexdigest() def parse_gray_color(cup: bytes) -> str: @@ -833,6 +870,18 @@ def is_typeshed_file(typeshed_dir: str | None, file: str) -> bool: return False +def is_stdlib_file(typeshed_dir: str | None, file: str) -> bool: + if "stdlib" not in file: + # Fast path + return False + typeshed_dir = typeshed_dir if typeshed_dir is not None else TYPESHED_DIR + stdlib_dir = os.path.join(typeshed_dir, "stdlib") + try: + return os.path.commonpath((stdlib_dir, os.path.abspath(file))) == stdlib_dir + except ValueError: # Different drives on Windows + return False + + def is_stub_package_file(file: str) -> bool: # Use hacky heuristics to check whether file is part of a PEP 561 stub package. if not file.endswith(".pyi"): @@ -874,3 +923,31 @@ def quote_docstring(docstr: str) -> str: return f"''{docstr_repr}''" else: return f'""{docstr_repr}""' + + +def json_dumps(obj: object, debug: bool = False) -> bytes: + if orjson is not None: + if debug: + dumps_option = orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS + else: + # TODO: If we don't sort keys here, testIncrementalInternalScramble fails + # We should document exactly what is going on there + dumps_option = orjson.OPT_SORT_KEYS + + try: + return orjson.dumps(obj, option=dumps_option) # type: ignore[no-any-return] + except TypeError as e: + if str(e) != "Integer exceeds 64-bit range": + raise + + if debug: + return json.dumps(obj, indent=2, sort_keys=True).encode("utf-8") + else: + # See above for sort_keys comment + return json.dumps(obj, sort_keys=True, separators=(",", ":")).encode("utf-8") + + +def json_loads(data: bytes) -> Any: + if orjson is not None: + return orjson.loads(data) + return json.loads(data) diff --git a/mypy/version.py b/mypy/version.py index f2615b77109da..dd02c98bb5c49 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -8,7 +8,7 @@ # - Release versions have the form "1.2.3". # - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440). # - Before 1.0 we had the form "0.NNN". -__version__ = "1.11.0+dev" +__version__ = "1.14.1" base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) diff --git a/mypy_bootstrap.ini b/mypy_bootstrap.ini index c680990fbd9ea..6e82f23b05305 100644 --- a/mypy_bootstrap.ini +++ b/mypy_bootstrap.ini @@ -1,16 +1,6 @@ [mypy] -disallow_untyped_calls = True -disallow_untyped_defs = True -disallow_incomplete_defs = True -check_untyped_defs = True -disallow_subclassing_any = True -warn_no_return = True -strict_optional = True -no_implicit_optional = True -disallow_any_generics = True -disallow_any_unimported = True -warn_redundant_casts = True -warn_unused_configs = True +strict = True +warn_unused_ignores = False show_traceback = True always_true = MYPYC diff --git a/mypyc/__main__.py b/mypyc/__main__.py index a3b9d21bc65a7..653199e0fb558 100644 --- a/mypyc/__main__.py +++ b/mypyc/__main__.py @@ -24,7 +24,7 @@ from mypyc.build import mypycify setup(name='mypyc_output', - ext_modules=mypycify({}, opt_level="{}", debug_level="{}"), + ext_modules=mypycify({}, opt_level="{}", debug_level="{}", strict_dunder_typing={}), ) """ @@ -38,10 +38,11 @@ def main() -> None: opt_level = os.getenv("MYPYC_OPT_LEVEL", "3") debug_level = os.getenv("MYPYC_DEBUG_LEVEL", "1") + strict_dunder_typing = bool(int(os.getenv("MYPYC_STRICT_DUNDER_TYPING", "0"))) setup_file = os.path.join(build_dir, "setup.py") with open(setup_file, "w") as f: - f.write(setup_format.format(sys.argv[1:], opt_level, debug_level)) + f.write(setup_format.format(sys.argv[1:], opt_level, debug_level, strict_dunder_typing)) # We don't use run_setup (like we do in the test suite) because it throws # away the error code from distutils, and we don't care about the slight diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 9babf860fb311..411fc80934048 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -5,7 +5,6 @@ from abc import abstractmethod from typing import Dict, Generic, Iterable, Iterator, Set, Tuple, TypeVar -from mypyc.ir.func_ir import all_values from mypyc.ir.ops import ( Assign, AssignMulti, @@ -437,27 +436,6 @@ def visit_set_mem(self, op: SetMem) -> GenAndKill[Value]: return set(), set() -def analyze_undefined_regs( - blocks: list[BasicBlock], cfg: CFG, initial_defined: set[Value] -) -> AnalysisResult[Value]: - """Calculate potentially undefined registers at each CFG location. - - A register is undefined if there is some path from initial block - where it has an undefined value. - - Function arguments are assumed to be always defined. - """ - initial_undefined = set(all_values([], blocks)) - initial_defined - return run_analysis( - blocks=blocks, - cfg=cfg, - gen_and_kill=UndefinedVisitor(), - initial=initial_undefined, - backward=False, - kind=MAYBE_ANALYSIS, - ) - - def non_trivial_sources(op: Op) -> set[Value]: result = set() for source in op.sources(): diff --git a/mypyc/build.py b/mypyc/build.py index 485803acba46f..6d59113ef8722 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -470,6 +470,7 @@ def mypycify( skip_cgen_input: Any | None = None, target_dir: str | None = None, include_runtime_files: bool | None = None, + strict_dunder_typing: bool = False, ) -> list[Extension]: """Main entry point to building using mypyc. @@ -509,6 +510,9 @@ def mypycify( should be directly #include'd instead of linked separately in order to reduce compiler invocations. Defaults to False in multi_file mode, True otherwise. + strict_dunder_typing: If True, force dunder methods to have the return type + of the method strictly, which can lead to more + optimization opportunities. Defaults to False. """ # Figure out our configuration @@ -519,6 +523,7 @@ def mypycify( separate=separate is not False, target_dir=target_dir, include_runtime_files=include_runtime_files, + strict_dunder_typing=strict_dunder_typing, ) # Generate all the actual important C code diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 8dcf7212b6946..d1a9ad3bace16 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -213,8 +213,7 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: methods_name = f"{name_prefix}_methods" vtable_setup_name = f"{name_prefix}_trait_vtable_setup" - fields: dict[str, str] = {} - fields["tp_name"] = f'"{name}"' + fields: dict[str, str] = {"tp_name": f'"{name}"'} generate_full = not cl.is_trait and not cl.builtin_base needs_getseters = cl.needs_getseters or not cl.is_generated or cl.has_dict @@ -571,6 +570,7 @@ def generate_setup_for_class( emitter.emit_line("}") else: emitter.emit_line(f"self->vtable = {vtable_name};") + for i in range(0, len(cl.bitmap_attrs), BITMAP_BITS): field = emitter.bitmap_field(i) emitter.emit_line(f"self->{field} = 0;") @@ -734,7 +734,7 @@ def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) - for attr, rtype in base.attributes.items(): emitter.emit_gc_visit(f"self->{emitter.attr(attr)}", rtype) if has_managed_dict(cl, emitter): - emitter.emit_line("_PyObject_VisitManagedDict((PyObject *)self, visit, arg);") + emitter.emit_line("PyObject_VisitManagedDict((PyObject *)self, visit, arg);") elif cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that @@ -757,7 +757,7 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> N for attr, rtype in base.attributes.items(): emitter.emit_gc_clear(f"self->{emitter.attr(attr)}", rtype) if has_managed_dict(cl, emitter): - emitter.emit_line("_PyObject_ClearManagedDict((PyObject *)self);") + emitter.emit_line("PyObject_ClearManagedDict((PyObject *)self);") elif cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index d945a28d84817..6088fb06dd32f 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -72,6 +72,7 @@ from mypyc.ir.pprint import generate_names_for_ir from mypyc.ir.rtypes import ( RArray, + RInstance, RStruct, RTuple, RType, @@ -362,20 +363,23 @@ def visit_get_attr(self, op: GetAttr) -> None: prefer_method = cl.is_trait and attr_rtype.error_overlap if cl.get_method(op.attr, prefer_method=prefer_method): # Properties are essentially methods, so use vtable access for them. - version = "_TRAIT" if cl.is_trait else "" - self.emit_line( - "%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */" - % ( - dest, - version, - obj, - self.emitter.type_struct_name(rtype.class_ir), - rtype.getter_index(op.attr), - rtype.struct_name(self.names), - self.ctype(rtype.attr_type(op.attr)), - op.attr, + if cl.is_method_final(op.attr): + self.emit_method_call(f"{dest} = ", op.obj, op.attr, []) + else: + version = "_TRAIT" if cl.is_trait else "" + self.emit_line( + "%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */" + % ( + dest, + version, + obj, + self.emitter.type_struct_name(rtype.class_ir), + rtype.getter_index(op.attr), + rtype.struct_name(self.names), + self.ctype(rtype.attr_type(op.attr)), + op.attr, + ) ) - ) else: # Otherwise, use direct or offset struct access. attr_expr = self.get_attr_expr(obj, op, decl_cl) @@ -529,11 +533,13 @@ def visit_call(self, op: Call) -> None: def visit_method_call(self, op: MethodCall) -> None: """Call native method.""" dest = self.get_dest_assign(op) - obj = self.reg(op.obj) + self.emit_method_call(dest, op.obj, op.method, op.args) - rtype = op.receiver_type + def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Value]) -> None: + obj = self.reg(op_obj) + rtype = op_obj.type + assert isinstance(rtype, RInstance) class_ir = rtype.class_ir - name = op.method method = rtype.class_ir.get_method(name) assert method is not None @@ -547,7 +553,7 @@ def visit_method_call(self, op: MethodCall) -> None: if method.decl.kind == FUNC_STATICMETHOD else [f"(PyObject *)Py_TYPE({obj})"] if method.decl.kind == FUNC_CLASSMETHOD else [obj] ) - args = ", ".join(obj_args + [self.reg(arg) for arg in op.args]) + args = ", ".join(obj_args + [self.reg(arg) for arg in op_args]) mtype = native_function_type(method, self.emitter) version = "_TRAIT" if rtype.class_ir.is_trait else "" if is_direct: @@ -567,7 +573,7 @@ def visit_method_call(self, op: MethodCall) -> None: rtype.struct_name(self.names), mtype, args, - op.method, + name, ) ) diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 1d8708912de5d..5b2812c2293a7 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -24,7 +24,7 @@ from mypy.nodes import MypyFile from mypy.options import Options from mypy.plugin import Plugin, ReportConfigContext -from mypy.util import hash_digest +from mypy.util import hash_digest, json_dumps from mypyc.codegen.cstring import c_string_initializer from mypyc.codegen.emit import Emitter, EmitterContext, HeaderDeclaration, c_array_initializer from mypyc.codegen.emitclass import generate_class, generate_class_type_decl @@ -47,7 +47,6 @@ use_vectorcall, ) from mypyc.errors import Errors -from mypyc.ir.class_ir import ClassIR from mypyc.ir.func_ir import FuncIR from mypyc.ir.module_ir import ModuleIR, ModuleIRs, deserialize_modules from mypyc.ir.ops import DeserMaps, LoadLiteral @@ -154,7 +153,7 @@ def report_config_data(self, ctx: ReportConfigContext) -> tuple[str | None, list ir_data = json.loads(ir_json) # Check that the IR cache matches the metadata cache - if compute_hash(meta_json) != ir_data["meta_hash"]: + if hash_digest(meta_json) != ir_data["meta_hash"]: return None # Check that all of the source files are present and as @@ -369,11 +368,11 @@ def write_cache( newpath = get_state_ir_cache_name(st) ir_data = { "ir": module.serialize(), - "meta_hash": compute_hash(meta_data), + "meta_hash": hash_digest(meta_data), "src_hashes": hashes[group_map[id]], } - result.manager.metastore.write(newpath, json.dumps(ir_data, separators=(",", ":"))) + result.manager.metastore.write(newpath, json_dumps(ir_data)) result.manager.metastore.commit() @@ -1075,20 +1074,6 @@ def declare_type_vars(self, module: str, type_var_names: list[str], emitter: Emi ) -def sort_classes(classes: list[tuple[str, ClassIR]]) -> list[tuple[str, ClassIR]]: - mod_name = {ir: name for name, ir in classes} - irs = [ir for _, ir in classes] - deps: dict[ClassIR, set[ClassIR]] = {} - for ir in irs: - if ir not in deps: - deps[ir] = set() - if ir.base: - deps[ir].add(ir.base) - deps[ir].update(ir.traits) - sorted_irs = toposort(deps) - return [(mod_name[ir], ir) for ir in sorted_irs] - - T = TypeVar("T") diff --git a/mypyc/codegen/literals.py b/mypyc/codegen/literals.py index 1f0c3bc6ec7bd..2c4ab0c1dc2ea 100644 --- a/mypyc/codegen/literals.py +++ b/mypyc/codegen/literals.py @@ -230,7 +230,7 @@ def format_int(n: int) -> bytes: def format_str_literal(s: str) -> bytes: - utf8 = s.encode("utf-8") + utf8 = s.encode("utf-8", errors="surrogatepass") return format_int(len(utf8)) + utf8 diff --git a/mypyc/doc/bytes_operations.rst b/mypyc/doc/bytes_operations.rst new file mode 100644 index 0000000000000..038da63919491 --- /dev/null +++ b/mypyc/doc/bytes_operations.rst @@ -0,0 +1,46 @@ +.. _bytes-ops: + +Native bytes operations +======================== + +These ``bytes`` operations have fast, optimized implementations. Other +bytes operations use generic implementations that are often slower. + +Construction +------------ + +* Bytes literal +* ``bytes(x: list)`` + +Operators +--------- + +* Concatenation (``b1 + b2``) +* Indexing (``b[n]``) +* Slicing (``b[n:m]``, ``b[n:]``, ``b[:m]``) +* Comparisons (``==``, ``!=``) + +.. _bytes-methods: + +Methods +------- + +* ``b.decode()`` +* ``b.decode(encoding: str)`` +* ``b.decode(encoding: str, errors: str)`` +* ``b.join(x: Iterable)`` + +.. note:: + + :ref:`str.encode() ` is also optimized. + +Formatting +---------- + +A subset of % formatting operations are optimized (``b"..." % (...)``). + +Functions +--------- + +* ``len(b: bytes)`` +* ``ord(b: bytes)`` diff --git a/mypyc/doc/conf.py b/mypyc/doc/conf.py index da887e0d82676..fdd98c12a221d 100644 --- a/mypyc/doc/conf.py +++ b/mypyc/doc/conf.py @@ -36,7 +36,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] # type: ignore[var-annotated] +extensions: list[str] = [] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index d11df7068e91c..461a19d371212 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -202,6 +202,10 @@ general overview of how things work. Test cases live under -q mypyc`. If you don't make changes to code under `mypy/`, it's not important to regularly run mypy tests during development. +You can use `python runtests.py mypyc-fast` to run a subset of mypyc +tests that covers most functionality but runs significantly quicker +than the entire test suite. + When you create a PR, we have Continuous Integration jobs set up that compile mypy using mypyc and run the mypy test suite using the compiled mypy. This will sometimes catch additional issues not caught diff --git a/mypyc/doc/index.rst b/mypyc/doc/index.rst index 5b1cc48fab3d1..584d6739e8030 100644 --- a/mypyc/doc/index.rst +++ b/mypyc/doc/index.rst @@ -36,6 +36,7 @@ generate fast code. bool_operations float_operations str_operations + bytes_operations list_operations dict_operations set_operations diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 5420c8af7d31c..9e94f1b6d7bbe 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -22,9 +22,14 @@ Operators * Comparisons (``==``, ``!=``) * Augmented assignment (``s1 += s2``) +.. _str-methods: + Methods ------- +* ``s.encode()`` +* ``s.encode(encoding: str)`` +* ``s.encode(encoding: str, errors: str)`` * ``s1.endswith(s2: str)`` * ``s.join(x: Iterable)`` * ``s.replace(old: str, new: str)`` @@ -33,3 +38,22 @@ Methods * ``s.split(sep: str)`` * ``s.split(sep: str, maxsplit: int)`` * ``s1.startswith(s2: str)`` + +.. note:: + + :ref:`bytes.decode() ` is also optimized. + +Formatting +---------- + +A subset of these common string formatting expressions are optimized: + +* F-strings +* ``"...".format(...)`` +* ``"..." % (...)`` + +Functions +--------- + +* ``len(s: str)`` +* ``ord(s: str)`` diff --git a/mypyc/external/googletest/src/gtest.cc b/mypyc/external/googletest/src/gtest.cc index d882ab2e36a12..4df3bd6b418ac 100644 --- a/mypyc/external/googletest/src/gtest.cc +++ b/mypyc/external/googletest/src/gtest.cc @@ -1784,7 +1784,7 @@ std::string CodePointToUtf8(UInt32 code_point) { return str; } -// The following two functions only make sense if the the system +// The following two functions only make sense if the system // uses UTF-16 for wide string encoding. All supported systems // with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 18f3cbcff987f..94bf714b28d48 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -93,6 +93,7 @@ def __init__( is_generated: bool = False, is_abstract: bool = False, is_ext_class: bool = True, + is_final_class: bool = False, ) -> None: self.name = name self.module_name = module_name @@ -100,6 +101,7 @@ def __init__( self.is_generated = is_generated self.is_abstract = is_abstract self.is_ext_class = is_ext_class + self.is_final_class = is_final_class # An augmented class has additional methods separate from what mypyc generates. # Right now the only one is dataclasses. self.is_augmented = False @@ -199,7 +201,8 @@ def __repr__(self) -> str: "ClassIR(" "name={self.name}, module_name={self.module_name}, " "is_trait={self.is_trait}, is_generated={self.is_generated}, " - "is_abstract={self.is_abstract}, is_ext_class={self.is_ext_class}" + "is_abstract={self.is_abstract}, is_ext_class={self.is_ext_class}, " + "is_final_class={self.is_final_class}" ")".format(self=self) ) @@ -248,8 +251,7 @@ def has_method(self, name: str) -> bool: def is_method_final(self, name: str) -> bool: subs = self.subclasses() if subs is None: - # TODO: Look at the final attribute! - return False + return self.is_final_class if self.has_method(name): method_decl = self.method_decl(name) @@ -349,6 +351,7 @@ def serialize(self) -> JsonDict: "is_abstract": self.is_abstract, "is_generated": self.is_generated, "is_augmented": self.is_augmented, + "is_final_class": self.is_final_class, "inherits_python": self.inherits_python, "has_dict": self.has_dict, "allow_interpreted_subclasses": self.allow_interpreted_subclasses, @@ -404,6 +407,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ClassIR: ir.is_abstract = data["is_abstract"] ir.is_ext_class = data["is_ext_class"] ir.is_augmented = data["is_augmented"] + ir.is_final_class = data["is_final_class"] ir.inherits_python = data["inherits_python"] ir.has_dict = data["has_dict"] ir.allow_interpreted_subclasses = data["allow_interpreted_subclasses"] diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 896ba3ac091c9..6e186c4ef0fc3 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -625,7 +625,7 @@ def __init__( assert error_kind == ERR_NEVER def __repr__(self) -> str: - return f"" + return f"" class PrimitiveOp(RegisterOp): diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index fecfaee5ef77e..53e3cee74e560 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -64,7 +64,7 @@ class RType: @abstractmethod def accept(self, visitor: RTypeVisitor[T]) -> T: - raise NotImplementedError + raise NotImplementedError() def short_name(self) -> str: return short_name(self.name) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index a9e1ce4719537..a0837ba2bfc79 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -84,6 +84,7 @@ IntOp, LoadStatic, Op, + PrimitiveDescription, RaiseStandardError, Register, SetAttr, @@ -381,6 +382,9 @@ def load_module(self, name: str) -> Value: def call_c(self, desc: CFunctionDescription, args: list[Value], line: int) -> Value: return self.builder.call_c(desc, args, line) + def primitive_op(self, desc: PrimitiveDescription, args: list[Value], line: int) -> Value: + return self.builder.primitive_op(desc, args, line) + def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.builder.int_op(type, lhs, rhs, op, line) @@ -400,7 +404,7 @@ def add_to_non_ext_dict( ) -> None: # Add an attribute entry into the class dict of a non-extension class. key_unicode = self.load_str(key) - self.call_c(dict_set_item_op, [non_ext.dict, key_unicode, val], line) + self.primitive_op(dict_set_item_op, [non_ext.dict, key_unicode, val], line) def gen_import(self, id: str, line: int) -> None: self.imports[id] = None @@ -431,7 +435,7 @@ def get_module(self, module: str, line: int) -> Value: # Python 3.7 has a nice 'PyImport_GetModule' function that we can't use :( mod_dict = self.call_c(get_module_dict_op, [], line) # Get module object from modules dict. - return self.call_c(dict_get_item_op, [mod_dict, self.load_str(module)], line) + return self.primitive_op(dict_get_item_op, [mod_dict, self.load_str(module)], line) def get_module_attr(self, module: str, attr: str, line: int) -> Value: """Look up an attribute of a module without storing it in the local namespace. @@ -594,7 +598,7 @@ def get_assignment_target( if isinstance(symbol, Decorator): symbol = symbol.func if symbol is None: - # New semantic analyzer doesn't create ad-hoc Vars for special forms. + # Semantic analyzer doesn't create ad-hoc Vars for special forms. assert lvalue.is_special_form symbol = Var(lvalue.name) if not for_read and isinstance(symbol, Var) and symbol.is_cls: @@ -691,7 +695,7 @@ def assign(self, target: Register | AssignmentTarget, rvalue_reg: Value, line: i else: key = self.load_str(target.attr) boxed_reg = self.builder.box(rvalue_reg) - self.call_c(py_setattr_op, [target.obj, key, boxed_reg], line) + self.primitive_op(py_setattr_op, [target.obj, key, boxed_reg], line) elif isinstance(target, AssignmentTargetIndex): target_reg2 = self.gen_method_call( target.base, "__setitem__", [target.index, rvalue_reg], None, line @@ -768,7 +772,7 @@ def process_iterator_tuple_assignment_helper( def process_iterator_tuple_assignment( self, target: AssignmentTargetTuple, rvalue_reg: Value, line: int ) -> None: - iterator = self.call_c(iter_op, [rvalue_reg], line) + iterator = self.primitive_op(iter_op, [rvalue_reg], line) # This may be the whole lvalue list if there is no starred value split_idx = target.star_idx if target.star_idx is not None else len(target.items) @@ -794,7 +798,7 @@ def process_iterator_tuple_assignment( # Assign the starred value and all values after it if target.star_idx is not None: post_star_vals = target.items[split_idx + 1 :] - iter_list = self.call_c(to_list, [iterator], line) + iter_list = self.primitive_op(to_list, [iterator], line) iter_list_len = self.builtin_len(iter_list, line) post_star_len = Integer(len(post_star_vals)) condition = self.binary_op(post_star_len, iter_list_len, "<=", line) @@ -813,7 +817,7 @@ def process_iterator_tuple_assignment( self.activate_block(ok_block) for litem in reversed(post_star_vals): - ritem = self.call_c(list_pop_last, [iter_list], line) + ritem = self.primitive_op(list_pop_last, [iter_list], line) self.assign(litem, ritem, line) # Assign the starred value @@ -979,17 +983,13 @@ def _analyze_iterable_item_type(self, expr: Expression) -> Type: def is_native_module(self, module: str) -> bool: """Is the given module one compiled by mypyc?""" - return module in self.mapper.group_map + return self.mapper.is_native_module(module) def is_native_ref_expr(self, expr: RefExpr) -> bool: - if expr.node is None: - return False - if "." in expr.node.fullname: - return self.is_native_module(expr.node.fullname.rpartition(".")[0]) - return True + return self.mapper.is_native_ref_expr(expr) def is_native_module_ref_expr(self, expr: RefExpr) -> bool: - return self.is_native_ref_expr(expr) and expr.kind == GDEF + return self.mapper.is_native_module_ref_expr(expr) def is_synthetic_type(self, typ: TypeInfo) -> bool: """Is a type something other than just a class we've created?""" @@ -1055,9 +1055,9 @@ def call_refexpr_with_args( # Handle data-driven special-cased primitive call ops. if callee.fullname and expr.arg_kinds == [ARG_POS] * len(arg_values): fullname = get_call_target_fullname(callee) - call_c_ops_candidates = function_ops.get(fullname, []) - target = self.builder.matching_call_c( - call_c_ops_candidates, arg_values, expr.line, self.node_type(expr) + primitive_candidates = function_ops.get(fullname, []) + target = self.builder.matching_primitive_op( + primitive_candidates, arg_values, expr.line, self.node_type(expr) ) if target: return target @@ -1302,7 +1302,7 @@ def load_global(self, expr: NameExpr) -> Value: def load_global_str(self, name: str, line: int) -> Value: _globals = self.load_globals_dict() reg = self.load_str(name) - return self.call_c(dict_get_item_op, [_globals, reg], line) + return self.primitive_op(dict_get_item_op, [_globals, reg], line) def load_globals_dict(self) -> Value: return self.add(LoadStatic(dict_rprimitive, "globals", self.module_name)) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 2152da099e819..6072efa2c593a 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -7,11 +7,13 @@ from typing import Callable, Final from mypy.nodes import ( + EXCLUDED_ENUM_ATTRIBUTES, TYPE_VAR_TUPLE_KIND, AssignmentStmt, CallExpr, ClassDef, Decorator, + EllipsisExpr, ExpressionStmt, FuncDef, Lvalue, @@ -26,7 +28,7 @@ TypeParam, is_class_var, ) -from mypy.types import ENUM_REMOVED_PROPS, Instance, RawExpressionType, get_proper_type +from mypy.types import Instance, UnboundType, get_proper_type from mypyc.common import PROPSET_PREFIX from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.ir.func_ir import FuncDecl, FuncSignature @@ -80,6 +82,7 @@ pytype_from_template_op, type_object_op, ) +from mypyc.subtype import is_subtype def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: @@ -145,7 +148,9 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: continue with builder.catch_errors(stmt.line): cls_builder.add_method(get_func_def(stmt)) - elif isinstance(stmt, PassStmt): + elif isinstance(stmt, PassStmt) or ( + isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, EllipsisExpr) + ): continue elif isinstance(stmt, AssignmentStmt): if len(stmt.lvalues) != 1: @@ -251,7 +256,7 @@ def finalize(self, ir: ClassIR) -> None: ) # Add the non-extension class to the dict - self.builder.call_c( + self.builder.primitive_op( dict_set_item_op, [ self.builder.load_globals_dict(), @@ -287,7 +292,7 @@ def add_attr(self, lvalue: NameExpr, stmt: AssignmentStmt) -> None: return typ = self.builder.load_native_type_object(self.cdef.fullname) value = self.builder.accept(stmt.rvalue) - self.builder.call_c( + self.builder.primitive_op( py_setattr_op, [typ, self.builder.load_str(lvalue.name), value], stmt.line ) if self.builder.non_function_scope() and stmt.is_final_def: @@ -447,7 +452,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: ) ) # Populate a '__mypyc_attrs__' field containing the list of attrs - builder.call_c( + builder.primitive_op( py_setattr_op, [ tp, @@ -461,7 +466,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add it to the dict - builder.call_c( + builder.primitive_op( dict_set_item_op, [builder.load_globals_dict(), builder.load_str(cdef.name), tp], cdef.line ) @@ -478,7 +483,7 @@ def make_generic_base_class( for tv, type_param in zip(tvs, type_args): if type_param.kind == TYPE_VAR_TUPLE_KIND: # Evaluate *Ts for a TypeVarTuple - it = builder.call_c(iter_op, [tv], line) + it = builder.primitive_op(iter_op, [tv], line) tv = builder.call_c(next_op, [it], line) args.append(tv) @@ -488,7 +493,7 @@ def make_generic_base_class( else: arg = builder.new_tuple(args, line) - base = builder.call_c(py_get_item_op, [gent, arg], line) + base = builder.primitive_op(py_get_item_op, [gent, arg], line) return base @@ -508,7 +513,7 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value: is_named_tuple = cdef.info.is_named_tuple ir = builder.mapper.type_to_ir[cdef.info] bases = [] - for cls in cdef.info.mro[1:]: + for cls in (b.type for b in cdef.info.bases): if cls.fullname == "builtins.object": continue if is_named_tuple and cls.fullname in ( @@ -598,7 +603,7 @@ def setup_non_ext_dict( This class dictionary is passed to the metaclass constructor. """ # Check if the metaclass defines a __prepare__ method, and if so, call it. - has_prepare = builder.call_c( + has_prepare = builder.primitive_op( py_hasattr_op, [metaclass, builder.load_str("__prepare__")], cdef.line ) @@ -640,22 +645,23 @@ def add_non_ext_class_attr_ann( if typ is None: # FIXME: if get_type_info is not provided, don't fall back to stmt.type? ann_type = get_proper_type(stmt.type) - if isinstance(stmt.unanalyzed_type, RawExpressionType) and isinstance( - stmt.unanalyzed_type.literal_value, str + if ( + isinstance(stmt.unanalyzed_type, UnboundType) + and stmt.unanalyzed_type.original_str_expr is not None ): # Annotation is a forward reference, so don't attempt to load the actual # type and load the string instead. # # TODO: is it possible to determine whether a non-string annotation is # actually a forward reference due to the __annotations__ future? - typ = builder.load_str(stmt.unanalyzed_type.literal_value) + typ = builder.load_str(stmt.unanalyzed_type.original_str_expr) elif isinstance(ann_type, Instance): typ = load_type(builder, ann_type.type, stmt.line) else: typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line)) key = builder.load_str(lvalue.name) - builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) + builder.primitive_op(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) def add_non_ext_class_attr( @@ -676,9 +682,9 @@ def add_non_ext_class_attr( # are final. if ( cdef.info.bases - and cdef.info.bases[0].type.fullname == "enum.Enum" + and cdef.info.bases[0].type.is_enum # Skip these since Enum will remove it - and lvalue.name not in ENUM_REMOVED_PROPS + and lvalue.name not in EXCLUDED_ENUM_ATTRIBUTES ): # Enum values are always boxed, so use object_rprimitive. attr_to_cache.append((lvalue, object_rprimitive)) @@ -797,30 +803,42 @@ def create_ne_from_eq(builder: IRBuilder, cdef: ClassDef) -> None: def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None: """Generate a "__ne__" method from a "__eq__" method.""" - with builder.enter_method(cls, "__ne__", object_rprimitive): - rhs_arg = builder.add_argument("rhs", object_rprimitive) - - # If __eq__ returns NotImplemented, then __ne__ should also - not_implemented_block, regular_block = BasicBlock(), BasicBlock() + func_ir = cls.get_method("__eq__") + assert func_ir + eq_sig = func_ir.decl.sig + strict_typing = builder.options.strict_dunders_typing + with builder.enter_method(cls, "__ne__", eq_sig.ret_type): + rhs_type = eq_sig.args[0].type if strict_typing else object_rprimitive + rhs_arg = builder.add_argument("rhs", rhs_type) eqval = builder.add(MethodCall(builder.self(), "__eq__", [rhs_arg], line)) - not_implemented = builder.add( - LoadAddress(not_implemented_op.type, not_implemented_op.src, line) - ) - builder.add( - Branch( - builder.translate_is_op(eqval, not_implemented, "is", line), - not_implemented_block, - regular_block, - Branch.BOOL, - ) - ) - builder.activate_block(regular_block) - retval = builder.coerce(builder.unary_op(eqval, "not", line), object_rprimitive, line) - builder.add(Return(retval)) + can_return_not_implemented = is_subtype(not_implemented_op.type, eq_sig.ret_type) + return_bool = is_subtype(eq_sig.ret_type, bool_rprimitive) - builder.activate_block(not_implemented_block) - builder.add(Return(not_implemented)) + if not strict_typing or can_return_not_implemented: + # If __eq__ returns NotImplemented, then __ne__ should also + not_implemented_block, regular_block = BasicBlock(), BasicBlock() + not_implemented = builder.add( + LoadAddress(not_implemented_op.type, not_implemented_op.src, line) + ) + builder.add( + Branch( + builder.translate_is_op(eqval, not_implemented, "is", line), + not_implemented_block, + regular_block, + Branch.BOOL, + ) + ) + builder.activate_block(regular_block) + rettype = bool_rprimitive if return_bool and strict_typing else object_rprimitive + retval = builder.coerce(builder.unary_op(eqval, "not", line), rettype, line) + builder.add(Return(retval)) + builder.activate_block(not_implemented_block) + builder.add(Return(not_implemented)) + else: + rettype = bool_rprimitive if return_bool and strict_typing else object_rprimitive + retval = builder.coerce(builder.unary_op(eqval, "not", line), rettype, line) + builder.add(Return(retval)) def load_non_ext_class( diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 8d7c089e20cd9..97cd31af93aff 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -10,6 +10,7 @@ from typing import Callable, Sequence from mypy.nodes import ( + ARG_NAMED, ARG_POS, LDEF, AssertTypeExpr, @@ -59,6 +60,7 @@ Integer, LoadAddress, LoadLiteral, + PrimitiveDescription, RaiseStandardError, Register, TupleGet, @@ -98,7 +100,7 @@ from mypyc.primitives.generic_ops import iter_op from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op -from mypyc.primitives.registry import CFunctionDescription, builtin_names +from mypyc.primitives.registry import builtin_names from mypyc.primitives.set_ops import set_add_op, set_in_op, set_update_op from mypyc.primitives.str_ops import str_slice_op from mypyc.primitives.tuple_ops import list_tuple_op, tuple_slice_op @@ -181,7 +183,7 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: # AST doesn't include a Var node for the module. We # instead load the module separately on each access. mod_dict = builder.call_c(get_module_dict_op, [], expr.line) - obj = builder.call_c( + obj = builder.primitive_op( dict_get_item_op, [mod_dict, builder.load_str(expr.node.fullname)], expr.line ) return obj @@ -355,6 +357,7 @@ def translate_method_call(builder: IRBuilder, expr: CallExpr, callee: MemberExpr and isinstance(callee.expr.node, TypeInfo) and callee.expr.node in builder.mapper.type_to_ir and builder.mapper.type_to_ir[callee.expr.node].has_method(callee.name) + and all(kind in (ARG_POS, ARG_NAMED) for kind in expr.arg_kinds) ): # Call a method via the *class* assert isinstance(callee.expr.node, TypeInfo) @@ -476,7 +479,7 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe # Grab first argument vself: Value = builder.self() if decl.kind == FUNC_CLASSMETHOD: - vself = builder.call_c(type_op, [vself], expr.line) + vself = builder.primitive_op(type_op, [vself], expr.line) elif builder.fn_info.is_generator: # For generator classes, the self target is the 6th value # in the symbol table (which is an ordered dict). This is sort @@ -953,7 +956,7 @@ def transform_tuple_expr(builder: IRBuilder, expr: TupleExpr) -> Value: def _visit_tuple_display(builder: IRBuilder, expr: TupleExpr) -> Value: """Create a list, then turn it into a tuple.""" val_as_list = _visit_list_display(builder, expr.items, expr.line) - return builder.call_c(list_tuple_op, [val_as_list], expr.line) + return builder.primitive_op(list_tuple_op, [val_as_list], expr.line) def transform_dict_expr(builder: IRBuilder, expr: DictExpr) -> Value: @@ -977,8 +980,8 @@ def _visit_display( builder: IRBuilder, items: list[Expression], constructor_op: Callable[[list[Value], int], Value], - append_op: CFunctionDescription, - extend_op: CFunctionDescription, + append_op: PrimitiveDescription, + extend_op: PrimitiveDescription, line: int, is_list: bool, ) -> Value: @@ -999,7 +1002,7 @@ def _visit_display( if result is None: result = constructor_op(initial_items, line) - builder.call_c(extend_op if starred else append_op, [result, value], line) + builder.primitive_op(extend_op if starred else append_op, [result, value], line) if result is None: result = constructor_op(initial_items, line) @@ -1028,7 +1031,7 @@ def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehe def gen_inner_stmts() -> None: k = builder.accept(o.key) v = builder.accept(o.value) - builder.call_c(dict_set_item_op, [builder.read(d), k, v], o.line) + builder.primitive_op(dict_set_item_op, [builder.read(d), k, v], o.line) comprehension_helper(builder, loop_params, gen_inner_stmts, o.line) return builder.read(d) @@ -1045,12 +1048,12 @@ def get_arg(arg: Expression | None) -> Value: return builder.accept(arg) args = [get_arg(expr.begin_index), get_arg(expr.end_index), get_arg(expr.stride)] - return builder.call_c(new_slice_op, args, expr.line) + return builder.primitive_op(new_slice_op, args, expr.line) def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value: builder.warning("Treating generator comprehension as list", o.line) - return builder.call_c(iter_op, [translate_list_comprehension(builder, o)], o.line) + return builder.primitive_op(iter_op, [translate_list_comprehension(builder, o)], o.line) def transform_assignment_expr(builder: IRBuilder, o: AssignmentExpr) -> Value: diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 5d8315e88f721..9b34a094db60f 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -251,7 +251,7 @@ def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Valu def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.call_c(list_append_op, [builder.read(list_ops), e], gen.line) + builder.primitive_op(list_append_op, [builder.read(list_ops), e], gen.line) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) return builder.read(list_ops) @@ -286,7 +286,7 @@ def translate_set_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.call_c(set_add_op, [builder.read(set_ops), e], gen.line) + builder.primitive_op(set_add_op, [builder.read(set_ops), e], gen.line) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) return builder.read(set_ops) @@ -586,7 +586,7 @@ def init(self, expr_reg: Value, target_type: RType) -> None: # for the for-loop. If we are inside of a generator function, spill these into the # environment class. builder = self.builder - iter_reg = builder.call_c(iter_op, [expr_reg], self.line) + iter_reg = builder.primitive_op(iter_op, [expr_reg], self.line) builder.maybe_spill(expr_reg) self.iter_target = builder.maybe_spill(iter_reg) self.target_type = target_type @@ -985,7 +985,6 @@ def init(self) -> None: zero = Integer(0) self.index_reg = builder.maybe_spill_assignable(zero) self.index_target: Register | AssignmentTarget = builder.get_assignment_target(self.index) - builder.assign(self.index_target, zero, self.line) def gen_step(self) -> None: builder = self.builder @@ -997,7 +996,9 @@ def gen_step(self) -> None: short_int_rprimitive, builder.read(self.index_reg, line), Integer(1), IntOp.ADD, line ) builder.assign(self.index_reg, new_val, line) - builder.assign(self.index_target, new_val, line) + + def begin_body(self) -> None: + self.builder.assign(self.index_target, self.builder.read(self.index_reg), self.line) class ForEnumerate(ForGenerator): diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index 0b46887811fb5..eaa4027ed7684 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -146,12 +146,12 @@ def convert_format_expr_to_str( if is_str_rprimitive(node_type): var_str = builder.accept(x) elif is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type): - var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line) + var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line) else: - var_str = builder.call_c(str_op, [builder.accept(x)], line) + var_str = builder.primitive_op(str_op, [builder.accept(x)], line) elif format_op == FormatOp.INT: if is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type): - var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line) + var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line) else: return None else: diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index c985e88b0e0cb..a84db5a08863b 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -96,7 +96,8 @@ def transform_func_def(builder: IRBuilder, fdef: FuncDef) -> None: - func_ir, func_reg = gen_func_item(builder, fdef, fdef.name, builder.mapper.fdef_to_sig(fdef)) + sig = builder.mapper.fdef_to_sig(fdef, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, fdef, fdef.name, sig) # If the function that was visited was a nested function, then either look it up in our # current environment or define it if it was not already defined. @@ -113,9 +114,8 @@ def transform_overloaded_func_def(builder: IRBuilder, o: OverloadedFuncDef) -> N def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: - func_ir, func_reg = gen_func_item( - builder, dec.func, dec.func.name, builder.mapper.fdef_to_sig(dec.func) - ) + sig = builder.mapper.fdef_to_sig(dec.func, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, dec.func, dec.func.name, sig) decorated_func: Value | None = None if func_reg: decorated_func = load_decorated_func(builder, dec.func, func_reg) @@ -133,7 +133,7 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: if decorated_func is not None: # Set the callable object representing the decorated function as a global. - builder.call_c( + builder.primitive_op( dict_set_item_op, [builder.load_globals_dict(), builder.load_str(dec.func.name), decorated_func], decorated_func.line, @@ -416,7 +416,8 @@ def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None # Perform the function of visit_method for methods inside extension classes. name = fdef.name class_ir = builder.mapper.type_to_ir[cdef.info] - func_ir, func_reg = gen_func_item(builder, fdef, name, builder.mapper.fdef_to_sig(fdef), cdef) + sig = builder.mapper.fdef_to_sig(fdef, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, fdef, name, sig, cdef) builder.functions.append(func_ir) if is_decorated(builder, fdef): @@ -432,7 +433,9 @@ def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None # Set the callable object representing the decorated method as an attribute of the # extension class. - builder.call_c(py_setattr_op, [typ, builder.load_str(name), decorated_func], fdef.line) + builder.primitive_op( + py_setattr_op, [typ, builder.load_str(name), decorated_func], fdef.line + ) if fdef.is_property: # If there is a property setter, it will be processed after the getter, @@ -481,7 +484,8 @@ def handle_non_ext_method( ) -> None: # Perform the function of visit_method for methods inside non-extension classes. name = fdef.name - func_ir, func_reg = gen_func_item(builder, fdef, name, builder.mapper.fdef_to_sig(fdef), cdef) + sig = builder.mapper.fdef_to_sig(fdef, builder.options.strict_dunders_typing) + func_ir, func_reg = gen_func_item(builder, fdef, name, sig, cdef) assert func_reg is not None builder.functions.append(func_ir) @@ -804,6 +808,11 @@ def load_type(builder: IRBuilder, typ: TypeInfo, line: int) -> Value: elif typ.fullname in builtin_names: builtin_addr_type, src = builtin_names[typ.fullname] class_obj = builder.add(LoadAddress(builtin_addr_type, src, line)) + elif typ.module_name in builder.imports: + loaded_module = builder.load_module(typ.module_name) + class_obj = builder.builder.get_attr( + loaded_module, typ.name, object_rprimitive, line, borrow=False + ) else: class_obj = builder.load_global_str(typ.name, line) @@ -840,7 +849,7 @@ def generate_singledispatch_dispatch_function( dispatch_func_obj, "dispatch_cache", dict_rprimitive, line ) call_find_impl, use_cache, call_func = BasicBlock(), BasicBlock(), BasicBlock() - get_result = builder.call_c(dict_get_method_with_none, [dispatch_cache, arg_type], line) + get_result = builder.primitive_op(dict_get_method_with_none, [dispatch_cache, arg_type], line) is_not_none = builder.translate_is_op(get_result, builder.none_object(), "is not", line) impl_to_use = Register(object_rprimitive) builder.add_bool_branch(is_not_none, use_cache, call_find_impl) @@ -853,7 +862,7 @@ def generate_singledispatch_dispatch_function( find_impl = builder.load_module_attr_by_fullname("functools._find_impl", line) registry = load_singledispatch_registry(builder, dispatch_func_obj, line) uncached_impl = builder.py_call(find_impl, [arg_type, registry], line) - builder.call_c(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line) + builder.primitive_op(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl], line) builder.assign(impl_to_use, uncached_impl, line) builder.goto(call_func) @@ -966,7 +975,7 @@ def generate_singledispatch_callable_class_ctor(builder: IRBuilder) -> None: cache_dict = builder.call_c(dict_new_op, [], line) dispatch_cache_str = builder.load_str("dispatch_cache") # use the py_setattr_op instead of SetAttr so that it also gets added to our __dict__ - builder.call_c(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line) + builder.primitive_op(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line) # the generated C code seems to expect that __init__ returns a char, so just return 1 builder.add(Return(Integer(1, bool_rprimitive, line), line)) @@ -988,10 +997,6 @@ def singledispatch_main_func_name(orig_name: str) -> str: return f"__mypyc_singledispatch_main_function_{orig_name}__" -def get_registry_identifier(fitem: FuncDef) -> str: - return f"__mypyc_singledispatch_registry_{fitem.fullname}__" - - def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: line = fitem.line is_singledispatch_main_func = fitem in builder.singledispatch_impls @@ -1013,7 +1018,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: registry_dict = builder.builder.make_dict([(loaded_object_type, main_func_obj)], line) dispatch_func_obj = builder.load_global_str(fitem.name, line) - builder.call_c( + builder.primitive_op( py_setattr_op, [dispatch_func_obj, builder.load_str("registry"), registry_dict], line ) @@ -1034,7 +1039,7 @@ def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: registry = load_singledispatch_registry(builder, dispatch_func_obj, line) for typ in types: loaded_type = load_type(builder, typ, line) - builder.call_c(dict_set_item_op, [registry, loaded_type, to_insert], line) + builder.primitive_op(dict_set_item_op, [registry, loaded_type, to_insert], line) dispatch_cache = builder.builder.get_attr( dispatch_func_obj, "dispatch_cache", dict_rprimitive, line ) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 0c9310e6a5cac..556d753b89f89 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -14,7 +14,7 @@ from mypy.argmap import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, ArgKind -from mypy.operators import op_methods +from mypy.operators import op_methods, unary_op_methods from mypy.types import AnyType, TypeOfAny from mypyc.common import ( BITMAP_BITS, @@ -167,6 +167,7 @@ buf_init_item, fast_isinstance_op, none_object_op, + not_implemented_op, var_object_size, ) from mypyc.primitives.registry import ( @@ -619,7 +620,7 @@ def py_get_attr(self, obj: Value, attr: str, line: int) -> Value: Prefer get_attr() which generates optimized code for native classes. """ key = self.load_str(attr) - return self.call_c(py_getattr_op, [obj, key], line) + return self.primitive_op(py_getattr_op, [obj, key], line) # isinstance() checks @@ -655,7 +656,9 @@ def isinstance_native(self, obj: Value, class_ir: ClassIR, line: int) -> Value: """ concrete = all_concrete_classes(class_ir) if concrete is None or len(concrete) > FAST_ISINSTANCE_MAX_SUBCLASSES + 1: - return self.call_c(fast_isinstance_op, [obj, self.get_native_type(class_ir)], line) + return self.primitive_op( + fast_isinstance_op, [obj, self.get_native_type(class_ir)], line + ) if not concrete: # There can't be any concrete instance that matches this. return self.false() @@ -759,7 +762,7 @@ def _construct_varargs( if kind == ARG_STAR: if star_result is None: star_result = self.new_list_op(star_values, line) - self.call_c(list_extend_op, [star_result, value], line) + self.primitive_op(list_extend_op, [star_result, value], line) elif kind == ARG_STAR2: if star2_result is None: star2_result = self._create_dict(star2_keys, star2_values, line) @@ -856,7 +859,7 @@ def _construct_varargs( if star_result is None: star_result = self.new_tuple(star_values, line) else: - star_result = self.call_c(list_tuple_op, [star_result], line) + star_result = self.primitive_op(list_tuple_op, [star_result], line) if has_star2 and star2_result is None: star2_result = self._create_dict(star2_keys, star2_values, line) @@ -1398,11 +1401,48 @@ def binary_op(self, lreg: Value, rreg: Value, op: str, line: int) -> Value: if base_op in float_op_to_id: return self.float_op(lreg, rreg, base_op, line) + dunder_op = self.dunder_op(lreg, rreg, op, line) + if dunder_op: + return dunder_op + primitive_ops_candidates = binary_ops.get(op, []) target = self.matching_primitive_op(primitive_ops_candidates, [lreg, rreg], line) assert target, "Unsupported binary operation: %s" % op return target + def dunder_op(self, lreg: Value, rreg: Value | None, op: str, line: int) -> Value | None: + """ + Dispatch a dunder method if applicable. + For example for `a + b` it will use `a.__add__(b)` which can lead to higher performance + due to the fact that the method could be already compiled and optimized instead of going + all the way through `PyNumber_Add(a, b)` python api (making a jump into the python DL). + """ + ltype = lreg.type + if not isinstance(ltype, RInstance): + return None + + method_name = op_methods.get(op) if rreg else unary_op_methods.get(op) + if method_name is None: + return None + + if not ltype.class_ir.has_method(method_name): + return None + + decl = ltype.class_ir.method_decl(method_name) + if not rreg and len(decl.sig.args) != 1: + return None + + if rreg and (len(decl.sig.args) != 2 or not is_subtype(rreg.type, decl.sig.args[1].type)): + return None + + if rreg and is_subtype(not_implemented_op.type, decl.sig.ret_type): + # If the method is able to return NotImplemented, we should not optimize it. + # We can just let go so it will be handled through the python api. + return None + + args = [rreg] if rreg else [] + return self.gen_method_call(lreg, method_name, args, decl.sig.ret_type, line) + def check_tagged_short_int(self, val: Value, line: int, negated: bool = False) -> Value: """Check if a tagged integer is a short integer. @@ -1477,7 +1517,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val # Cast to bool if necessary since most types uses comparison returning a object type # See generic_ops.py for more information if not is_bool_rprimitive(compare.type): - compare = self.call_c(bool_op, [compare], line) + compare = self.primitive_op(bool_op, [compare], line) if i < len(lhs.type.types) - 1: branch = Branch(compare, early_stop, check_blocks[i + 1], Branch.BOOL) else: @@ -1496,7 +1536,7 @@ def compare_tuples(self, lhs: Value, rhs: Value, op: str, line: int = -1) -> Val def translate_instance_contains(self, inst: Value, item: Value, op: str, line: int) -> Value: res = self.gen_method_call(inst, "__contains__", [item], None, line) if not is_bool_rprimitive(res.type): - res = self.call_c(bool_op, [res], line) + res = self.primitive_op(bool_op, [res], line) if op == "not in": res = self.bool_bitwise_op(res, Integer(1, rtype=bool_rprimitive), "^", line) return res @@ -1558,18 +1598,11 @@ def unary_op(self, value: Value, expr_op: str, line: int) -> Value: if isinstance(value, Float): return Float(-value.value, value.line) if isinstance(typ, RInstance): - if expr_op == "-": - method = "__neg__" - elif expr_op == "+": - method = "__pos__" - elif expr_op == "~": - method = "__invert__" - else: - method = "" - if method and typ.class_ir.has_method(method): - return self.gen_method_call(value, method, [], None, line) - call_c_ops_candidates = unary_ops.get(expr_op, []) - target = self.matching_call_c(call_c_ops_candidates, [value], line) + result = self.dunder_op(value, None, expr_op, line) + if result is not None: + return result + primitive_ops_candidates = unary_ops.get(expr_op, []) + target = self.matching_primitive_op(primitive_ops_candidates, [value], line) assert target, "Unsupported unary operation: %s" % expr_op return target @@ -1636,7 +1669,7 @@ def new_list_op(self, values: list[Value], line: int) -> Value: return result_list def new_set_op(self, values: list[Value], line: int) -> Value: - return self.call_c(new_set_op, values, line) + return self.primitive_op(new_set_op, values, line) def setup_rarray( self, item_type: RType, values: Sequence[Value], *, object_ptr: bool = False @@ -1744,7 +1777,7 @@ def bool_value(self, value: Value) -> Value: self.goto(end) self.activate_block(end) else: - result = self.call_c(bool_op, [value], value.line) + result = self.primitive_op(bool_op, [value], value.line) return result def add_bool_branch(self, value: Value, true: BasicBlock, false: BasicBlock) -> None: @@ -1889,7 +1922,7 @@ def primitive_op( # Does this primitive map into calling a Python C API # or an internal mypyc C API function? if desc.c_function_name: - # TODO: Generate PrimitiOps here and transform them into CallC + # TODO: Generate PrimitiveOps here and transform them into CallC # ops only later in the lowering pass c_desc = CFunctionDescription( desc.name, @@ -1906,9 +1939,9 @@ def primitive_op( desc.priority, is_pure=desc.is_pure, ) - return self.call_c(c_desc, args, line, result_type) + return self.call_c(c_desc, args, line, result_type=result_type) - # This primitve gets transformed in a lowering pass to + # This primitive gets transformed in a lowering pass to # lower-level IR ops using a custom transform function. coerced = [] @@ -1972,7 +2005,7 @@ def matching_primitive_op( else: matching = desc if matching: - return self.primitive_op(matching, args, line=line) + return self.primitive_op(matching, args, line=line, result_type=result_type) return None def int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1) -> Value: @@ -2034,7 +2067,7 @@ def float_mod(self, lhs: Value, rhs: Value, line: int) -> Value: self.activate_block(copysign) # If the remainder is zero, CPython ensures the result has the # same sign as the denominator. - adj = self.call_c(copysign_op, [Float(0.0), rhs], line) + adj = self.primitive_op(copysign_op, [Float(0.0), rhs], line) self.add(Assign(res, adj)) self.add(Goto(done)) self.activate_block(done) @@ -2229,7 +2262,7 @@ def new_tuple_with_length(self, length: Value, line: int) -> Value: return self.call_c(new_tuple_with_length_op, [length], line) def int_to_float(self, n: Value, line: int) -> Value: - return self.call_c(int_to_float_op, [n], line) + return self.primitive_op(int_to_float_op, [n], line) # Internal helpers @@ -2313,11 +2346,11 @@ def translate_special_method_call( Return None if no translation found; otherwise return the target register. """ - call_c_ops_candidates = method_call_ops.get(name, []) - call_c_op = self.matching_call_c( - call_c_ops_candidates, [base_reg] + args, line, result_type, can_borrow=can_borrow + primitive_ops_candidates = method_call_ops.get(name, []) + primitive_op = self.matching_primitive_op( + primitive_ops_candidates, [base_reg] + args, line, result_type, can_borrow=can_borrow ) - return call_c_op + return primitive_op def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) -> Value | None: """Add a equality comparison operation. diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 90ce0e16c7418..9cd263c40ae42 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -62,6 +62,7 @@ def __init__(self, group_map: dict[str, str | None]) -> None: self.group_map = group_map self.type_to_ir: dict[TypeInfo, ClassIR] = {} self.func_to_decl: dict[SymbolNode, FuncDecl] = {} + self.symbol_fullnames: set[str] = set() def type_to_rtype(self, typ: Type | None) -> RType: if typ is None: @@ -160,7 +161,7 @@ def get_arg_rtype(self, typ: Type, kind: ArgKind) -> RType: else: return self.type_to_rtype(typ) - def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature: + def fdef_to_sig(self, fdef: FuncDef, strict_dunders_typing: bool) -> FuncSignature: if isinstance(fdef.type, CallableType): arg_types = [ self.get_arg_rtype(typ, kind) @@ -199,11 +200,14 @@ def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature: ) ] - # We force certain dunder methods to return objects to support letting them - # return NotImplemented. It also avoids some pointless boxing and unboxing, - # since tp_richcompare needs an object anyways. - if fdef.name in ("__eq__", "__ne__", "__lt__", "__gt__", "__le__", "__ge__"): - ret = object_rprimitive + if not strict_dunders_typing: + # We force certain dunder methods to return objects to support letting them + # return NotImplemented. It also avoids some pointless boxing and unboxing, + # since tp_richcompare needs an object anyways. + # However, it also prevents some optimizations. + if fdef.name in ("__eq__", "__ne__", "__lt__", "__gt__", "__le__", "__ge__"): + ret = object_rprimitive + return FuncSignature(args, ret) def is_native_module(self, module: str) -> bool: @@ -214,7 +218,8 @@ def is_native_ref_expr(self, expr: RefExpr) -> bool: if expr.node is None: return False if "." in expr.node.fullname: - return self.is_native_module(expr.node.fullname.rpartition(".")[0]) + name = expr.node.fullname.rpartition(".")[0] + return self.is_native_module(name) or name in self.symbol_fullnames return True def is_native_module_ref_expr(self, expr: RefExpr) -> bool: diff --git a/mypyc/irbuild/match.py b/mypyc/irbuild/match.py index a1e671911ea59..976a8810b3278 100644 --- a/mypyc/irbuild/match.py +++ b/mypyc/irbuild/match.py @@ -131,7 +131,7 @@ def visit_class_pattern(self, pattern: ClassPattern) -> None: else slow_isinstance_op ) - cond = self.builder.call_c( + cond = self.builder.primitive_op( isinstance_op, [self.subject, self.builder.accept(pattern.class_ref)], pattern.line ) @@ -246,7 +246,7 @@ def visit_mapping_pattern(self, pattern: MappingPattern) -> None: self.builder.activate_block(self.code_block) self.code_block = BasicBlock() - rest = self.builder.call_c(dict_copy, [self.subject], pattern.rest.line) + rest = self.builder.primitive_op(dict_copy, [self.subject], pattern.rest.line) target = self.builder.get_assignment_target(pattern.rest) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 29e06439abdd6..4b132bb837221 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -81,7 +81,11 @@ def build_type_map( # references even if there are import cycles. for module, cdef in classes: class_ir = ClassIR( - cdef.name, module.fullname, is_trait(cdef), is_abstract=cdef.info.is_abstract + cdef.name, + module.fullname, + is_trait(cdef), + is_abstract=cdef.info.is_abstract, + is_final_class=cdef.info.is_final, ) class_ir.is_ext_class = is_extension_class(cdef) if class_ir.is_ext_class: @@ -90,14 +94,17 @@ def build_type_map( if not options.global_opts: class_ir.children = None mapper.type_to_ir[cdef.info] = class_ir + mapper.symbol_fullnames.add(class_ir.fullname) # Populate structural information in class IR for extension classes. for module, cdef in classes: with catch_errors(module.path, cdef.line): if mapper.type_to_ir[cdef.info].is_ext_class: - prepare_class_def(module.path, module.fullname, cdef, errors, mapper) + prepare_class_def(module.path, module.fullname, cdef, errors, mapper, options) else: - prepare_non_ext_class_def(module.path, module.fullname, cdef, errors, mapper) + prepare_non_ext_class_def( + module.path, module.fullname, cdef, errors, mapper, options + ) # Prepare implicit attribute accessors as needed if an attribute overrides a property. for module, cdef in classes: @@ -110,7 +117,7 @@ def build_type_map( # is conditionally defined. for module in modules: for func in get_module_func_defs(module): - prepare_func_def(module.fullname, None, func, mapper) + prepare_func_def(module.fullname, None, func, mapper, options) # TODO: what else? # Check for incompatible attribute definitions that were not @@ -143,6 +150,7 @@ def load_type_map(mapper: Mapper, modules: list[MypyFile], deser_ctx: DeserMaps) if isinstance(node.node, TypeInfo) and is_from_module(node.node, module): ir = deser_ctx.classes[node.node.fullname] mapper.type_to_ir[node.node] = ir + mapper.symbol_fullnames.add(node.node.fullname) mapper.func_to_decl[node.node] = ir.ctor for module in modules: @@ -164,27 +172,39 @@ def get_module_func_defs(module: MypyFile) -> Iterable[FuncDef]: def prepare_func_def( - module_name: str, class_name: str | None, fdef: FuncDef, mapper: Mapper + module_name: str, + class_name: str | None, + fdef: FuncDef, + mapper: Mapper, + options: CompilerOptions, ) -> FuncDecl: kind = ( FUNC_STATICMETHOD if fdef.is_static else (FUNC_CLASSMETHOD if fdef.is_class else FUNC_NORMAL) ) - decl = FuncDecl(fdef.name, class_name, module_name, mapper.fdef_to_sig(fdef), kind) + sig = mapper.fdef_to_sig(fdef, options.strict_dunders_typing) + decl = FuncDecl(fdef.name, class_name, module_name, sig, kind) mapper.func_to_decl[fdef] = decl return decl def prepare_method_def( - ir: ClassIR, module_name: str, cdef: ClassDef, mapper: Mapper, node: FuncDef | Decorator + ir: ClassIR, + module_name: str, + cdef: ClassDef, + mapper: Mapper, + node: FuncDef | Decorator, + options: CompilerOptions, ) -> None: if isinstance(node, FuncDef): - ir.method_decls[node.name] = prepare_func_def(module_name, cdef.name, node, mapper) + ir.method_decls[node.name] = prepare_func_def( + module_name, cdef.name, node, mapper, options + ) elif isinstance(node, Decorator): # TODO: do something about abstract methods here. Currently, they are handled just like # normal methods. - decl = prepare_func_def(module_name, cdef.name, node.func, mapper) + decl = prepare_func_def(module_name, cdef.name, node.func, mapper, options) if not node.decorators: ir.method_decls[node.name] = decl elif isinstance(node.decorators[0], MemberExpr) and node.decorators[0].name == "setter": @@ -237,7 +257,12 @@ def can_subclass_builtin(builtin_base: str) -> bool: def prepare_class_def( - path: str, module_name: str, cdef: ClassDef, errors: Errors, mapper: Mapper + path: str, + module_name: str, + cdef: ClassDef, + errors: Errors, + mapper: Mapper, + options: CompilerOptions, ) -> None: """Populate the interface-level information in a class IR. @@ -304,7 +329,7 @@ def prepare_class_def( ir.mro = mro ir.base_mro = base_mro - prepare_methods_and_attributes(cdef, ir, path, module_name, errors, mapper) + prepare_methods_and_attributes(cdef, ir, path, module_name, errors, mapper, options) prepare_init_method(cdef, ir, module_name, mapper) for base in bases: @@ -316,7 +341,13 @@ def prepare_class_def( def prepare_methods_and_attributes( - cdef: ClassDef, ir: ClassIR, path: str, module_name: str, errors: Errors, mapper: Mapper + cdef: ClassDef, + ir: ClassIR, + path: str, + module_name: str, + errors: Errors, + mapper: Mapper, + options: CompilerOptions, ) -> None: """Populate attribute and method declarations.""" info = cdef.info @@ -338,20 +369,20 @@ def prepare_methods_and_attributes( add_setter_declaration(ir, name, attr_rtype, module_name) ir.attributes[name] = attr_rtype elif isinstance(node.node, (FuncDef, Decorator)): - prepare_method_def(ir, module_name, cdef, mapper, node.node) + prepare_method_def(ir, module_name, cdef, mapper, node.node, options) elif isinstance(node.node, OverloadedFuncDef): # Handle case for property with both a getter and a setter if node.node.is_property: if is_valid_multipart_property_def(node.node): for item in node.node.items: - prepare_method_def(ir, module_name, cdef, mapper, item) + prepare_method_def(ir, module_name, cdef, mapper, item, options) else: errors.error("Unsupported property decorator semantics", path, cdef.line) # Handle case for regular function overload else: assert node.node.impl - prepare_method_def(ir, module_name, cdef, mapper, node.node.impl) + prepare_method_def(ir, module_name, cdef, mapper, node.node.impl, options) if ir.builtin_base: ir.attributes.clear() @@ -436,7 +467,7 @@ def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: M # Set up a constructor decl init_node = cdef.info["__init__"].node if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef): - init_sig = mapper.fdef_to_sig(init_node) + init_sig = mapper.fdef_to_sig(init_node, True) defining_ir = mapper.type_to_ir.get(init_node.info) # If there is a nontrivial __init__ that wasn't defined in an @@ -463,24 +494,29 @@ def prepare_init_method(cdef: ClassDef, ir: ClassIR, module_name: str, mapper: M def prepare_non_ext_class_def( - path: str, module_name: str, cdef: ClassDef, errors: Errors, mapper: Mapper + path: str, + module_name: str, + cdef: ClassDef, + errors: Errors, + mapper: Mapper, + options: CompilerOptions, ) -> None: ir = mapper.type_to_ir[cdef.info] info = cdef.info for node in info.names.values(): if isinstance(node.node, (FuncDef, Decorator)): - prepare_method_def(ir, module_name, cdef, mapper, node.node) + prepare_method_def(ir, module_name, cdef, mapper, node.node, options) elif isinstance(node.node, OverloadedFuncDef): # Handle case for property with both a getter and a setter if node.node.is_property: if not is_valid_multipart_property_def(node.node): errors.error("Unsupported property decorator semantics", path, cdef.line) for item in node.node.items: - prepare_method_def(ir, module_name, cdef, mapper, item) + prepare_method_def(ir, module_name, cdef, mapper, item, options) # Handle case for regular function overload else: - prepare_method_def(ir, module_name, cdef, mapper, get_func_def(node.node)) + prepare_method_def(ir, module_name, cdef, mapper, get_func_def(node.node), options) if any(cls in mapper.type_to_ir and mapper.type_to_ir[cls].is_ext_class for cls in info.mro): errors.error( diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 7c59584578866..f652449f52891 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -19,6 +19,7 @@ from mypy.nodes import ( ARG_NAMED, ARG_POS, + BytesExpr, CallExpr, DictExpr, Expression, @@ -89,6 +90,11 @@ dict_values_op, ) from mypyc.primitives.list_ops import new_list_set_item_op +from mypyc.primitives.str_ops import ( + str_encode_ascii_strict, + str_encode_latin1_strict, + str_encode_utf8_strict, +) from mypyc.primitives.tuple_ops import new_tuple_set_item_op # Specializers are attempted before compiling the arguments to the @@ -682,6 +688,58 @@ def translate_fstring(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Va return None +@specialize_function("encode", str_rprimitive) +def str_encode_fast_path(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + """Specialize common cases of str.encode for most used encodings and strict errors.""" + + if not isinstance(callee, MemberExpr): + return None + + # We can only specialize if we have string literals as args + if len(expr.arg_kinds) > 0 and not isinstance(expr.args[0], StrExpr): + return None + if len(expr.arg_kinds) > 1 and not isinstance(expr.args[1], StrExpr): + return None + + encoding = "utf8" + errors = "strict" + if len(expr.arg_kinds) > 0 and isinstance(expr.args[0], StrExpr): + if expr.arg_kinds[0] == ARG_NAMED: + if expr.arg_names[0] == "encoding": + encoding = expr.args[0].value + elif expr.arg_names[0] == "errors": + errors = expr.args[0].value + elif expr.arg_kinds[0] == ARG_POS: + encoding = expr.args[0].value + else: + return None + if len(expr.arg_kinds) > 1 and isinstance(expr.args[1], StrExpr): + if expr.arg_kinds[1] == ARG_NAMED: + if expr.arg_names[1] == "encoding": + encoding = expr.args[1].value + elif expr.arg_names[1] == "errors": + errors = expr.args[1].value + elif expr.arg_kinds[1] == ARG_POS: + errors = expr.args[1].value + else: + return None + + if errors != "strict": + # We can only specialize strict errors + return None + + encoding = encoding.lower().replace("-", "").replace("_", "") # normalize + # Specialized encodings and their accepted aliases + if encoding in ["u8", "utf", "utf8", "cp65001"]: + return builder.call_c(str_encode_utf8_strict, [builder.accept(callee.expr)], expr.line) + elif encoding in ["646", "ascii", "usascii"]: + return builder.call_c(str_encode_ascii_strict, [builder.accept(callee.expr)], expr.line) + elif encoding in ["iso88591", "8859", "cp819", "latin", "latin1", "l1"]: + return builder.call_c(str_encode_latin1_strict, [builder.accept(callee.expr)], expr.line) + + return None + + @specialize_function("mypy_extensions.i64") def translate_i64(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: @@ -820,3 +878,13 @@ def translate_float(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Valu # No-op float conversion. return builder.accept(arg) return None + + +@specialize_function("builtins.ord") +def translate_ord(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: + if len(expr.args) != 1 or expr.arg_kinds[0] != ARG_POS: + return None + arg = expr.args[0] + if isinstance(arg, (StrExpr, BytesExpr)) and len(arg.value) == 1: + return Integer(ord(arg.value)) + return None diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 4d828b1b9d823..bd4acccf077af 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -58,6 +58,7 @@ LoadLiteral, LoadStatic, MethodCall, + PrimitiveDescription, RaiseStandardError, Register, Return, @@ -347,10 +348,10 @@ def transform_import_from(builder: IRBuilder, node: ImportFrom) -> None: return module_state = builder.graph[builder.module_name] - if module_state.ancestors is not None and module_state.ancestors: - module_package = module_state.ancestors[0] - elif builder.module_path.endswith("__init__.py"): + if builder.module_path.endswith("__init__.py"): module_package = builder.module_name + elif module_state.ancestors is not None and module_state.ancestors: + module_package = module_state.ancestors[0] else: module_package = "" @@ -757,7 +758,7 @@ def transform_with( value = builder.add(MethodCall(mgr_v, f"__{al}enter__", args=[], line=line)) exit_ = None else: - typ = builder.call_c(type_op, [mgr_v], line) + typ = builder.primitive_op(type_op, [mgr_v], line) exit_ = builder.maybe_spill(builder.py_get_attr(typ, f"__{al}exit__", line)) value = builder.py_call(builder.py_get_attr(typ, f"__{al}enter__", line), [mgr_v], line) @@ -876,7 +877,7 @@ def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) line, ) key = builder.load_str(target.attr) - builder.call_c(py_delattr_op, [target.obj, key], line) + builder.primitive_op(py_delattr_op, [target.obj, key], line) elif isinstance(target, AssignmentTargetRegister): # Delete a local by assigning an error value to it, which will # prompt the insertion of uninit checks. @@ -924,7 +925,10 @@ def emit_yield_from_or_await( received_reg = Register(object_rprimitive) get_op = coro_op if is_await else iter_op - iter_val = builder.call_c(get_op, [val], line) + if isinstance(get_op, PrimitiveDescription): + iter_val = builder.primitive_op(get_op, [val], line) + else: + iter_val = builder.call_c(get_op, [val], line) iter_reg = builder.maybe_spill_assignable(iter_val) diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index ed01a59d1214e..e27e509ad7fa3 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -27,10 +27,16 @@ UnaryExpr, Var, ) +from mypy.semanal import refers_to_fullname +from mypy.types import FINAL_DECORATOR_NAMES DATACLASS_DECORATORS = {"dataclasses.dataclass", "attr.s", "attr.attrs"} +def is_final_decorator(d: Expression) -> bool: + return refers_to_fullname(d, FINAL_DECORATOR_NAMES) + + def is_trait_decorator(d: Expression) -> bool: return isinstance(d, RefExpr) and d.fullname == "mypy_extensions.trait" @@ -119,7 +125,10 @@ def get_mypyc_attrs(stmt: ClassDef | Decorator) -> dict[str, Any]: def is_extension_class(cdef: ClassDef) -> bool: if any( - not is_trait_decorator(d) and not is_dataclass_decorator(d) and not get_mypyc_attr_call(d) + not is_trait_decorator(d) + and not is_dataclass_decorator(d) + and not get_mypyc_attr_call(d) + and not is_final_decorator(d) for d in cdef.decorators ): return False diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 2ec04e4c5b5cc..d3637cde49ffd 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -730,6 +730,7 @@ bool CPyStr_IsTrue(PyObject *obj); Py_ssize_t CPyStr_Size_size_t(PyObject *str); PyObject *CPy_Decode(PyObject *obj, PyObject *encoding, PyObject *errors); PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors); +CPyTagged CPyStr_Ord(PyObject *obj); // Bytes operations @@ -740,6 +741,7 @@ PyObject *CPyBytes_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end); CPyTagged CPyBytes_GetItem(PyObject *o, CPyTagged index); PyObject *CPyBytes_Concat(PyObject *a, PyObject *b); PyObject *CPyBytes_Join(PyObject *sep, PyObject *iter); +CPyTagged CPyBytes_Ord(PyObject *obj); int CPyBytes_Compare(PyObject *left, PyObject *right); @@ -846,10 +848,7 @@ static inline bool CPy_TypeCheck(PyObject *o, PyObject *type) { return PyObject_TypeCheck(o, (PyTypeObject *)type); } -static inline PyObject *CPy_CalculateMetaclass(PyObject *type, PyObject *o) { - return (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)type, o); -} - +PyObject *CPy_CalculateMetaclass(PyObject *type, PyObject *o); PyObject *CPy_GetCoro(PyObject *obj); PyObject *CPyIter_Send(PyObject *iter, PyObject *val); int CPy_YieldFromErrorHandle(PyObject *iter, PyObject **outp); diff --git a/mypyc/lib-rt/bytes_ops.c b/mypyc/lib-rt/bytes_ops.c index 4da62be115718..5ddf3528211f2 100644 --- a/mypyc/lib-rt/bytes_ops.c +++ b/mypyc/lib-rt/bytes_ops.c @@ -99,7 +99,7 @@ PyObject *CPyBytes_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) { // (mostly commonly, for bytearrays) PyObject *CPyBytes_Join(PyObject *sep, PyObject *iter) { if (PyBytes_CheckExact(sep)) { - return _PyBytes_Join(sep, iter); + return PyBytes_Join(sep, iter); } else { _Py_IDENTIFIER(join); return _PyObject_CallMethodIdOneArg(sep, &PyId_join, iter); @@ -141,3 +141,20 @@ PyObject *CPyBytes_Build(Py_ssize_t len, ...) { return (PyObject *)ret; } + + +CPyTagged CPyBytes_Ord(PyObject *obj) { + if (PyBytes_Check(obj)) { + Py_ssize_t s = PyBytes_GET_SIZE(obj); + if (s == 1) { + return (unsigned char)(PyBytes_AS_STRING(obj)[0]) << 1; + } + } else if (PyByteArray_Check(obj)) { + Py_ssize_t s = PyByteArray_GET_SIZE(obj); + if (s == 1) { + return (unsigned char)(PyByteArray_AS_STRING(obj)[0]) << 1; + } + } + PyErr_SetString(PyExc_TypeError, "ord() expects a character"); + return CPY_INT_TAG; +} diff --git a/mypyc/lib-rt/dict_ops.c b/mypyc/lib-rt/dict_ops.c index c0cc8d5a7f879..b33233521afda 100644 --- a/mypyc/lib-rt/dict_ops.c +++ b/mypyc/lib-rt/dict_ops.c @@ -78,7 +78,11 @@ PyObject *CPyDict_SetDefault(PyObject *dict, PyObject *key, PyObject *value) { return ret; } _Py_IDENTIFIER(setdefault); - return _PyObject_CallMethodIdObjArgs(dict, &PyId_setdefault, key, value, NULL); + PyObject *name = _PyUnicode_FromId(&PyId_setdefault); /* borrowed */ + if (name == NULL) { + return NULL; + } + return PyObject_CallMethodObjArgs(dict, name, key, value, NULL); } PyObject *CPyDict_SetDefaultWithNone(PyObject *dict, PyObject *key) { @@ -133,7 +137,11 @@ static inline int CPy_ObjectToStatus(PyObject *obj) { static int CPyDict_UpdateGeneral(PyObject *dict, PyObject *stuff) { _Py_IDENTIFIER(update); - PyObject *res = _PyObject_CallMethodIdOneArg(dict, &PyId_update, stuff); + PyObject *name = _PyUnicode_FromId(&PyId_update); /* borrowed */ + if (name == NULL) { + return -1; + } + PyObject *res = PyObject_CallMethodOneArg(dict, name, stuff); return CPy_ObjectToStatus(res); } @@ -230,12 +238,11 @@ PyObject *CPyDict_Keys(PyObject *dict) { if (view == NULL) { return NULL; } - PyObject *res = _PyList_Extend((PyListObject *)list, view); + int res = PyList_Extend(list, view); Py_DECREF(view); - if (res == NULL) { + if (res < 0) { return NULL; } - Py_DECREF(res); return list; } @@ -250,12 +257,11 @@ PyObject *CPyDict_Values(PyObject *dict) { if (view == NULL) { return NULL; } - PyObject *res = _PyList_Extend((PyListObject *)list, view); + int res = PyList_Extend(list, view); Py_DECREF(view); - if (res == NULL) { + if (res < 0) { return NULL; } - Py_DECREF(res); return list; } @@ -270,12 +276,11 @@ PyObject *CPyDict_Items(PyObject *dict) { if (view == NULL) { return NULL; } - PyObject *res = _PyList_Extend((PyListObject *)list, view); + int res = PyList_Extend(list, view); Py_DECREF(view); - if (res == NULL) { + if (res < 0) { return NULL; } - Py_DECREF(res); return list; } diff --git a/mypyc/lib-rt/getargs.c b/mypyc/lib-rt/getargs.c index 3c8b528f8048e..163b9ac2b1637 100644 --- a/mypyc/lib-rt/getargs.c +++ b/mypyc/lib-rt/getargs.c @@ -247,16 +247,15 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, #endif if (!skip) { if (i < nargs && i < max) { - current_arg = PyTuple_GET_ITEM(args, i); + current_arg = Py_NewRef(PyTuple_GET_ITEM(args, i)); } else if (nkwargs && i >= pos) { - current_arg = _PyDict_GetItemStringWithError(kwargs, kwlist[i]); + if (unlikely(PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0)) { + return 0; + } if (current_arg) { --nkwargs; } - else if (PyErr_Occurred()) { - return 0; - } } else { current_arg = NULL; @@ -265,6 +264,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (current_arg) { PyObject **p = va_arg(*p_va, PyObject **); *p = current_arg; + Py_DECREF(current_arg); format++; continue; } @@ -370,8 +370,12 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, Py_ssize_t j; /* make sure there are no arguments given by name and position */ for (i = pos; i < bound_pos_args && i < len; i++) { - current_arg = _PyDict_GetItemStringWithError(kwargs, kwlist[i]); + PyObject *current_arg; + if (unlikely(PyDict_GetItemStringRef(kwargs, kwlist[i], ¤t_arg) < 0)) { + goto latefail; + } if (unlikely(current_arg != NULL)) { + Py_DECREF(current_arg); /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, "argument for %.200s%s given by name ('%s') " @@ -381,9 +385,6 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, kwlist[i], i+1); goto latefail; } - else if (unlikely(PyErr_Occurred() != NULL)) { - goto latefail; - } } /* make sure there are no extraneous keyword arguments */ j = 0; @@ -395,7 +396,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, goto latefail; } for (i = pos; i < len; i++) { - if (CPyUnicode_EqualToASCIIString(key, kwlist[i])) { + if (PyUnicode_EqualToUTF8(key, kwlist[i])) { match = 1; break; } diff --git a/mypyc/lib-rt/getargsfast.c b/mypyc/lib-rt/getargsfast.c index 387deed4399b3..e5667e22efe3b 100644 --- a/mypyc/lib-rt/getargsfast.c +++ b/mypyc/lib-rt/getargsfast.c @@ -271,7 +271,7 @@ find_keyword(PyObject *kwnames, PyObject *const *kwstack, PyObject *key) for (i = 0; i < nkwargs; i++) { PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); assert(PyUnicode_Check(kwname)); - if (_PyUnicode_EQ(kwname, key)) { + if (PyUnicode_Equal(kwname, key)) { return kwstack[i]; } } diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c index df87228a0d100..d297ece8f4170 100644 --- a/mypyc/lib-rt/list_ops.c +++ b/mypyc/lib-rt/list_ops.c @@ -256,7 +256,10 @@ int CPyList_Insert(PyObject *list, CPyTagged index, PyObject *value) } PyObject *CPyList_Extend(PyObject *o1, PyObject *o2) { - return _PyList_Extend((PyListObject *)o1, o2); + if (PyList_Extend(o1, o2) < 0) { + return NULL; + } + Py_RETURN_NONE; } // Return -2 or error, -1 if not found, or index of first match otherwise. diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c index 803123d436a2e..d3e8e69ed19b9 100644 --- a/mypyc/lib-rt/misc_ops.c +++ b/mypyc/lib-rt/misc_ops.c @@ -131,6 +131,52 @@ static bool _CPy_IsSafeMetaClass(PyTypeObject *metaclass) { return matches; } +#if CPY_3_13_FEATURES + +// Adapted from CPython 3.13.0b3 +/* Determine the most derived metatype. */ +PyObject *CPy_CalculateMetaclass(PyObject *metatype, PyObject *bases) +{ + Py_ssize_t i, nbases; + PyTypeObject *winner; + PyObject *tmp; + PyTypeObject *tmptype; + + /* Determine the proper metatype to deal with this, + and check for metatype conflicts while we're at it. + Note that if some other metatype wins to contract, + it's possible that its instances are not types. */ + + nbases = PyTuple_GET_SIZE(bases); + winner = (PyTypeObject *)metatype; + for (i = 0; i < nbases; i++) { + tmp = PyTuple_GET_ITEM(bases, i); + tmptype = Py_TYPE(tmp); + if (PyType_IsSubtype(winner, tmptype)) + continue; + if (PyType_IsSubtype(tmptype, winner)) { + winner = tmptype; + continue; + } + /* else: */ + PyErr_SetString(PyExc_TypeError, + "metaclass conflict: " + "the metaclass of a derived class " + "must be a (non-strict) subclass " + "of the metaclasses of all its bases"); + return NULL; + } + return (PyObject *)winner; +} + +#else + +PyObject *CPy_CalculateMetaclass(PyObject *metatype, PyObject *bases) { + return (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)metatype, bases); +} + +#endif + // Create a heap type based on a template non-heap type. // This is super hacky and maybe we should suck it up and use PyType_FromSpec instead. // We allow bases to be NULL to represent just inheriting from object. @@ -163,7 +209,7 @@ PyObject *CPyType_FromTemplate(PyObject *template, // Find the appropriate metaclass from our base classes. We // care about this because Generic uses a metaclass prior to // Python 3.7. - metaclass = _PyType_CalculateMetaclass(metaclass, bases); + metaclass = (PyTypeObject *)CPy_CalculateMetaclass((PyObject *)metaclass, bases); if (!metaclass) goto error; @@ -517,7 +563,7 @@ int CPyStatics_Initialize(PyObject **statics, while (num-- > 0) { size_t len; data = parse_int(data, &len); - PyObject *obj = PyUnicode_FromStringAndSize(data, len); + PyObject *obj = PyUnicode_DecodeUTF8(data, len, "surrogatepass"); if (obj == NULL) { return -1; } diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 3c888a581a33f..9967f0a13b4f3 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -115,4 +115,7 @@ static inline void CPyLong_SetUnsignedSize(PyLongObject *o, Py_ssize_t n) { #endif +// Are we targeting Python 3.13 or newer? +#define CPY_3_13_FEATURES (PY_VERSION_HEX >= 0x030d0000) + #endif diff --git a/mypyc/lib-rt/pythoncapi_compat.h b/mypyc/lib-rt/pythoncapi_compat.h index 1b59f93de7ece..acaadf34bf2e4 100644 --- a/mypyc/lib-rt/pythoncapi_compat.h +++ b/mypyc/lib-rt/pythoncapi_compat.h @@ -45,6 +45,13 @@ extern "C" { # define _PyObject_CAST(op) _Py_CAST(PyObject*, op) #endif +#ifndef Py_BUILD_ASSERT +# define Py_BUILD_ASSERT(cond) \ + do { \ + (void)sizeof(char [1 - 2 * !(cond)]); \ + } while(0) +#endif + // bpo-42262 added Py_NewRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) @@ -1338,9 +1345,169 @@ PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, } #endif +#if PY_VERSION_HEX < 0x030D00B3 +# define Py_BEGIN_CRITICAL_SECTION(op) { +# define Py_END_CRITICAL_SECTION() } +# define Py_BEGIN_CRITICAL_SECTION2(a, b) { +# define Py_END_CRITICAL_SECTION2() } +#endif + +#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) +typedef struct PyUnicodeWriter PyUnicodeWriter; + +static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +{ + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); + PyMem_Free(writer); +} + +static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) +{ + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length must be positive"); + return NULL; + } + + const size_t size = sizeof(_PyUnicodeWriter); + PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); + if (pub_writer == _Py_NULL) { + PyErr_NoMemory(); + return _Py_NULL; + } + _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + + _PyUnicodeWriter_Init(writer); + if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { + PyUnicodeWriter_Discard(pub_writer); + return NULL; + } + writer->overallocate = 1; + return pub_writer; +} + +static inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +{ + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); + assert(((_PyUnicodeWriter*)writer)->buffer == NULL); + PyMem_Free(writer); + return str; +} + +static inline int +PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) +{ + if (ch > 0x10ffff) { + PyErr_SetString(PyExc_ValueError, + "character must be in range(0x110000)"); + return -1; + } + + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); +} + +static inline int +PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Str(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Repr(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + PyObject *str_obj = PyUnicode_FromStringAndSize(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, + const wchar_t *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)wcslen(str); + } + + PyObject *str_obj = PyUnicode_FromWideChar(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + if (!PyUnicode_Check(str)) { + PyErr_Format(PyExc_TypeError, "expect str, not %T", str); + return -1; + } + if (start < 0 || start > end) { + PyErr_Format(PyExc_ValueError, "invalid start argument"); + return -1; + } + if (end > PyUnicode_GET_LENGTH(str)) { + PyErr_Format(PyExc_ValueError, "invalid end argument"); + return -1; + } + + return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, + start, end); +} + +static inline int +PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyUnicode_FromFormatV(format, vargs); + va_end(vargs); + if (str == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030E0000 -// gh-116560 added PyLong_GetSign() to Python 3.14a4 -#if PY_VERSION_HEX < 0x030E00A1 +// gh-116560 added PyLong_GetSign() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 static inline int PyLong_GetSign(PyObject *obj, int *sign) { if (!PyLong_Check(obj)) { @@ -1354,6 +1521,175 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign) #endif +// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) +{ + if (!PyUnicode_Check(str1)) { + PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", + Py_TYPE(str1)->tp_name); + return -1; + } + if (!PyUnicode_Check(str2)) { + PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", + Py_TYPE(str2)->tp_name); + return -1; + } + +#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) + PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2); + + return _PyUnicode_Equal(str1, str2); +#elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#else + return (PyUnicode_Compare(str1, str2) == 0); +#endif +} +#endif + + +// gh-121645 added PyBytes_Join() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) +{ + return _PyBytes_Join(sep, iterable); +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) +{ +#if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) + PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); + + return _Py_HashBytes(ptr, len); +#else + Py_hash_t hash; + PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len); + if (bytes == NULL) { + return -1; + } + hash = PyObject_Hash(bytes); + Py_DECREF(bytes); + return hash; +#endif +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyIter_NextItem(PyObject *iter, PyObject **item) +{ + iternextfunc tp_iternext; + + assert(iter != NULL); + assert(item != NULL); + + tp_iternext = Py_TYPE(iter)->tp_iternext; + if (tp_iternext == NULL) { + *item = NULL; + PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", + Py_TYPE(iter)->tp_name); + return -1; + } + + if ((*item = tp_iternext(iter))) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + return 0; + } + return -1; +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyLong_FromInt32(int32_t value) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + return PyLong_FromLong(value); +} + +static inline PyObject* PyLong_FromInt64(int64_t value) +{ + Py_BUILD_ASSERT(sizeof(long long) >= 8); + return PyLong_FromLongLong(value); +} + +static inline PyObject* PyLong_FromUInt32(uint32_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long) >= 4); + return PyLong_FromUnsignedLong(value); +} + +static inline PyObject* PyLong_FromUInt64(uint64_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8); + return PyLong_FromUnsignedLongLong(value); +} + +static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(int) == 4); + int value = PyLong_AsInt(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int32_t)value; + return 0; +} + +static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + long long value = PyLong_AsLongLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int64_t)value; + return 0; +} + +static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + unsigned long value = PyLong_AsUnsignedLong(obj); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + return -1; + } +#if SIZEOF_LONG > 4 + if ((unsigned long)UINT32_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint32_t"); + return -1; + } +#endif + *pvalue = (uint32_t)value; + return 0; +} + +static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + unsigned long long value = PyLong_AsUnsignedLongLong(obj); + if (value == (unsigned long long)-1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (uint64_t)value; + return 0; +} +#endif + + #ifdef __cplusplus } #endif diff --git a/mypyc/lib-rt/pythonsupport.h b/mypyc/lib-rt/pythonsupport.h index 85f9ec64ac908..61929f5126082 100644 --- a/mypyc/lib-rt/pythonsupport.h +++ b/mypyc/lib-rt/pythonsupport.h @@ -13,6 +13,16 @@ #include #include "mypyc_util.h" +#if CPY_3_13_FEATURES +#ifndef Py_BUILD_CORE +#define Py_BUILD_CORE +#endif +#include "internal/pycore_call.h" // _PyObject_CallMethodIdNoArgs, _PyObject_CallMethodIdOneArg +#include "internal/pycore_genobject.h" // _PyGen_FetchStopIterationValue +#include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause, _PyErr_SetKeyError +#include "internal/pycore_setobject.h" // _PySet_Update +#endif + #if CPY_3_12_FEATURES #include "internal/pycore_frame.h" #endif @@ -48,7 +58,7 @@ update_bases(PyObject *bases) } continue; } - if (_PyObject_LookupAttrId(base, &PyId___mro_entries__, &meth) < 0) { + if (PyObject_GetOptionalAttrString(base, PyId___mro_entries__.string, &meth) < 0) { goto error; } if (!meth) { @@ -59,7 +69,7 @@ update_bases(PyObject *bases) } continue; } - new_base = _PyObject_FastCall(meth, stack, 1); + new_base = _PyObject_Vectorcall(meth, stack, 1, NULL); Py_DECREF(meth); if (!new_base) { goto error; @@ -108,7 +118,7 @@ init_subclass(PyTypeObject *type, PyObject *kwds) PyObject *super, *func, *result; PyObject *args[2] = {(PyObject *)type, (PyObject *)type}; - super = _PyObject_FastCall((PyObject *)&PySuper_Type, args, 2); + super = _PyObject_Vectorcall((PyObject *)&PySuper_Type, args, 2, NULL); if (super == NULL) { return -1; } @@ -307,8 +317,6 @@ list_count(PyListObject *self, PyObject *value) return CPyTagged_ShortFromSsize_t(count); } -#define CPyUnicode_EqualToASCIIString(x, y) _PyUnicode_EqualToASCIIString(x, y) - // Adapted from genobject.c in Python 3.7.2 // Copied because it wasn't in 3.5.2 and it is undocumented anyways. /* @@ -374,7 +382,7 @@ _CPyDictView_New(PyObject *dict, PyTypeObject *type) static int _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { PyObject *tmp = NULL; - int result = _PyObject_LookupAttrId(v, name, &tmp); + int result = PyObject_GetOptionalAttrString(v, name->string, &tmp); if (tmp) { Py_DECREF(tmp); } @@ -390,24 +398,19 @@ _CPyObject_HasAttrId(PyObject *v, _Py_Identifier *name) { _PyObject_CallMethodIdObjArgs((self), (name), NULL) #define _PyObject_CallMethodIdOneArg(self, name, arg) \ _PyObject_CallMethodIdObjArgs((self), (name), (arg), NULL) +#define PyObject_CallMethodOneArg(self, name, arg) \ + PyObject_CallMethodObjArgs((self), (name), (arg), NULL) #endif #if CPY_3_12_FEATURES // These are copied from genobject.c in Python 3.12 -/* Returns a borrowed reference */ -static inline PyCodeObject * -_PyGen_GetCode(PyGenObject *gen) { - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); - return frame->f_code; -} - static int gen_is_coroutine(PyObject *o) { if (PyGen_CheckExact(o)) { - PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o); + PyCodeObject *code = PyGen_GetCode((PyGenObject*)o); if (code->co_flags & CO_ITERABLE_COROUTINE) { return 1; } diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py index 66b130581cb30..1faacc8fc136b 100644 --- a/mypyc/lib-rt/setup.py +++ b/mypyc/lib-rt/setup.py @@ -21,7 +21,7 @@ compile_args = ["--std=c++11"] -class build_ext_custom(build_ext): +class build_ext_custom(build_ext): # noqa: N801 def get_library_names(self): return ["gtest"] diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c index 90b19001f8f0f..68026037502da 100644 --- a/mypyc/lib-rt/str_ops.c +++ b/mypyc/lib-rt/str_ops.c @@ -117,7 +117,11 @@ PyObject *CPyStr_Build(Py_ssize_t len, ...) { PyObject *item = va_arg(args, PyObject *); Py_ssize_t itemlen = PyUnicode_GET_LENGTH(item); if (itemlen != 0) { +#if CPY_3_13_FEATURES + PyUnicode_CopyCharacters(res, res_offset, item, 0, itemlen); +#else _PyUnicode_FastCopyCharacters(res, res_offset, item, 0, itemlen); +#endif res_offset += itemlen; } } @@ -239,3 +243,15 @@ PyObject *CPy_Encode(PyObject *obj, PyObject *encoding, PyObject *errors) { return NULL; } } + + +CPyTagged CPyStr_Ord(PyObject *obj) { + Py_ssize_t s = PyUnicode_GET_LENGTH(obj); + if (s == 1) { + int kind = PyUnicode_KIND(obj); + return PyUnicode_READ(kind, PyUnicode_DATA(obj), 0) << 1; + } + PyErr_Format( + PyExc_TypeError, "ord() expected a character, but a string of length %zd found", s); + return CPY_INT_TAG; +} diff --git a/mypyc/namegen.py b/mypyc/namegen.py index 675dae9001c71..ce84fde143d15 100644 --- a/mypyc/namegen.py +++ b/mypyc/namegen.py @@ -100,10 +100,9 @@ def make_module_translation_map(names: list[str]) -> dict[str, str]: for name in names: for suffix in candidate_suffixes(name): if num_instances[suffix] == 1: - result[name] = suffix break - else: - assert False, names + # Takes the last suffix if none are unique + result[name] = suffix return result diff --git a/mypyc/options.py b/mypyc/options.py index 5f0cf12aeefed..24e68163bb11f 100644 --- a/mypyc/options.py +++ b/mypyc/options.py @@ -14,6 +14,7 @@ def __init__( include_runtime_files: bool | None = None, capi_version: tuple[int, int] | None = None, python_version: tuple[int, int] | None = None, + strict_dunder_typing: bool = False, ) -> None: self.strip_asserts = strip_asserts self.multi_file = multi_file @@ -30,3 +31,10 @@ def __init__( # features are used. self.capi_version = capi_version or sys.version_info[:2] self.python_version = python_version + # Make possible to inline dunder methods in the generated code. + # Typically, the convention is the dunder methods can return `NotImplemented` + # even when its return type is just `bool`. + # By enabling this option, this convention is no longer valid and the dunder + # will assume the return type of the method strictly, which can lead to + # more optimization opportunities. + self.strict_dunders_typing = strict_dunder_typing diff --git a/mypyc/primitives/bytes_ops.py b/mypyc/primitives/bytes_ops.py index d7a7f3e2f59b8..1afd196cff846 100644 --- a/mypyc/primitives/bytes_ops.py +++ b/mypyc/primitives/bytes_ops.py @@ -99,3 +99,11 @@ error_kind=ERR_MAGIC, var_arg_type=bytes_rprimitive, ) + +function_op( + name="builtins.ord", + arg_types=[bytes_rprimitive], + return_type=int_rprimitive, + c_function_name="CPyBytes_Ord", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 2eff233403f48..657578d200460 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -31,14 +31,7 @@ str_rprimitive, void_rtype, ) -from mypyc.primitives.registry import ( - CFunctionDescription, - binary_op, - custom_op, - function_op, - load_address_op, - unary_op, -) +from mypyc.primitives.registry import binary_op, custom_op, function_op, load_address_op, unary_op # Constructors for builtins.int and native int types have the same behavior. In # interpreted mode, native int types are just aliases to 'int'. @@ -176,7 +169,7 @@ def int_binary_op( int_binary_op("<<=", "CPyTagged_Lshift", error_kind=ERR_MAGIC) -def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: +def int_unary_op(name: str, c_function_name: str) -> PrimitiveDescription: return unary_op( name=name, arg_type=int_rprimitive, diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 5190b01adf4a4..5e7ecb70f55d8 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -70,17 +70,17 @@ class LoadAddressDescription(NamedTuple): src: str # name of the target to load -# CallC op for method call(such as 'str.join') -method_call_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for method call (such as 'str.join') +method_call_ops: dict[str, list[PrimitiveDescription]] = {} -# CallC op for top level function call(such as 'builtins.list') -function_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for top level function call (such as 'builtins.list') +function_ops: dict[str, list[PrimitiveDescription]] = {} -# CallC op for binary ops +# Primitive ops for binary operations binary_ops: dict[str, list[PrimitiveDescription]] = {} -# CallC op for unary ops -unary_ops: dict[str, list[CFunctionDescription]] = {} +# Primitive ops for unary ops +unary_ops: dict[str, list[PrimitiveDescription]] = {} builtin_names: dict[str, tuple[RType, str]] = {} @@ -99,7 +99,7 @@ def method_op( is_borrowed: bool = False, priority: int = 1, is_pure: bool = False, -) -> CFunctionDescription: +) -> PrimitiveDescription: """Define a c function call op that replaces a method call. This will be automatically generated by matching against the AST. @@ -129,7 +129,7 @@ def method_op( if extra_int_constants is None: extra_int_constants = [] ops = method_call_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, arg_types, return_type, @@ -161,8 +161,8 @@ def function_op( steals: StealsDescription = False, is_borrowed: bool = False, priority: int = 1, -) -> CFunctionDescription: - """Define a c function call op that replaces a function call. +) -> PrimitiveDescription: + """Define a C function call op that replaces a function call. This will be automatically generated by matching against the AST. @@ -175,19 +175,19 @@ def function_op( if extra_int_constants is None: extra_int_constants = [] ops = function_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, arg_types, return_type, - var_arg_type, - truncated_type, - c_function_name, - error_kind, - steals, - is_borrowed, - ordering, - extra_int_constants, - priority, + var_arg_type=var_arg_type, + truncated_type=truncated_type, + c_function_name=c_function_name, + error_kind=error_kind, + steals=steals, + is_borrowed=is_borrowed, + ordering=ordering, + extra_int_constants=extra_int_constants, + priority=priority, is_pure=False, ) ops.append(desc) @@ -327,8 +327,8 @@ def unary_op( is_borrowed: bool = False, priority: int = 1, is_pure: bool = False, -) -> CFunctionDescription: - """Define a c function call op for an unary operation. +) -> PrimitiveDescription: + """Define a primitive op for an unary operation. This will be automatically generated by matching against the AST. @@ -338,19 +338,19 @@ def unary_op( if extra_int_constants is None: extra_int_constants = [] ops = unary_ops.setdefault(name, []) - desc = CFunctionDescription( + desc = PrimitiveDescription( name, [arg_type], return_type, - None, - truncated_type, - c_function_name, - error_kind, - steals, - is_borrowed, - ordering, - extra_int_constants, - priority, + var_arg_type=None, + truncated_type=truncated_type, + c_function_name=c_function_name, + error_kind=error_kind, + steals=steals, + is_borrowed=is_borrowed, + ordering=ordering, + extra_int_constants=extra_int_constants, + priority=priority, is_pure=is_pure, ) ops.append(desc) diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 2ff1fbdb4b3ed..0accffd86a170 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -219,6 +219,30 @@ extra_int_constants=[(0, pointer_rprimitive)], ) +# str.encode(encoding) - utf8 strict specialization +str_encode_utf8_strict = custom_op( + arg_types=[str_rprimitive], + return_type=bytes_rprimitive, + c_function_name="PyUnicode_AsUTF8String", + error_kind=ERR_MAGIC, +) + +# str.encode(encoding) - ascii strict specialization +str_encode_ascii_strict = custom_op( + arg_types=[str_rprimitive], + return_type=bytes_rprimitive, + c_function_name="PyUnicode_AsASCIIString", + error_kind=ERR_MAGIC, +) + +# str.encode(encoding) - latin1 strict specialization +str_encode_latin1_strict = custom_op( + arg_types=[str_rprimitive], + return_type=bytes_rprimitive, + c_function_name="PyUnicode_AsLatin1String", + error_kind=ERR_MAGIC, +) + # str.encode(encoding, errors) method_op( name="encode", @@ -227,3 +251,11 @@ c_function_name="CPy_Encode", error_kind=ERR_MAGIC, ) + +function_op( + name="builtins.ord", + arg_types=[str_rprimitive], + return_type=int_rprimitive, + c_function_name="CPyStr_Ord", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index ac95ffe2c047a..be66307286fc2 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -110,7 +110,7 @@ def upper(self) -> str: ... def startswith(self, x: str, start: int=..., end: int=...) -> bool: ... def endswith(self, x: str, start: int=..., end: int=...) -> bool: ... def replace(self, old: str, new: str, maxcount: int=...) -> str: ... - def encode(self, x: str=..., y: str=...) -> bytes: ... + def encode(self, encoding: str=..., errors: str=...) -> bytes: ... class float: def __init__(self, x: object) -> None: pass diff --git a/mypyc/test-data/fixtures/testutil.py b/mypyc/test-data/fixtures/testutil.py index f210faf711091..36ec41c8f38b2 100644 --- a/mypyc/test-data/fixtures/testutil.py +++ b/mypyc/test-data/fixtures/testutil.py @@ -45,7 +45,7 @@ def assertRaises(typ: type, msg: str = '') -> Iterator[None]: try: yield except Exception as e: - assert isinstance(e, typ), f"{e!r} is not a {typ.__name__}" + assert type(e) is typ, f"{e!r} is not a {typ.__name__}" assert msg in str(e), f'Message "{e}" does not match "{msg}"' else: assert False, f"Expected {typ.__name__} but got no exception" diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 0d14e1a5dfc82..3bfb1587fb3b1 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -151,7 +151,9 @@ def f4(a, n, b): a :: object n :: int b :: bool - r0, r1, r2, r3 :: object + r0 :: union[object, int] + r1, r2 :: object + r3 :: union[int, object] r4 :: int L0: if b goto L1 else goto L2 :: bool diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index b1a712103e70b..9082cc0136d9e 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -182,13 +182,31 @@ def int_to_int(n): L0: return n -[case testIntUnaryPlus] +[case testIntUnaryOps] +def unary_minus(n: int) -> int: + x = -n + return x def unary_plus(n: int) -> int: x = +n return x +def unary_invert(n: int) -> int: + x = ~n + return x [out] +def unary_minus(n): + n, r0, x :: int +L0: + r0 = CPyTagged_Negate(n) + x = r0 + return x def unary_plus(n): n, x :: int L0: x = n return x +def unary_invert(n): + n, r0, x :: int +L0: + r0 = CPyTagged_Invert(n) + x = r0 + return x diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index f9d3354b317c2..825bc750f7a7e 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -864,18 +864,16 @@ def g(x: Iterable[int]) -> None: [out] def f(a): a :: list - r0 :: short_int - i :: int - r1 :: short_int + r0, r1 :: short_int r2 :: native_int r3 :: short_int r4 :: bit + i :: int r5 :: object r6, x, r7 :: int r8, r9 :: short_int L0: r0 = 0 - i = 0 r1 = 0 L1: r2 = var_object_size a @@ -883,6 +881,7 @@ L1: r4 = int_lt r1, r3 if r4 goto L2 else goto L4 :: bool L2: + i = r0 r5 = CPyList_GetItemUnsafe(a, r1) r6 = unbox(int, r5) x = r6 @@ -890,7 +889,6 @@ L2: L3: r8 = r0 + 2 r0 = r8 - i = r8 r9 = r1 + 2 r1 = r9 goto L1 @@ -900,25 +898,23 @@ L5: def g(x): x :: object r0 :: short_int - i :: int r1, r2 :: object - r3, n :: int + i, r3, n :: int r4 :: short_int r5 :: bit L0: r0 = 0 - i = 0 r1 = PyObject_GetIter(x) L1: r2 = PyIter_Next(r1) if is_error(r2) goto L4 else goto L2 L2: + i = r0 r3 = unbox(int, r2) n = r3 L3: r4 = r0 + 2 r0 = r4 - i = r4 goto L1 L4: r5 = CPy_NoErrOccured() diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 771dcc4c0e68c..d17c66bba22f7 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -293,20 +293,136 @@ L0: def f(s: str) -> None: s.encode() s.encode('utf-8') + s.encode('utf8', 'strict') + s.encode('latin1', errors='strict') + s.encode(encoding='ascii') + s.encode(errors='strict', encoding='latin-1') + s.encode('utf-8', 'backslashreplace') s.encode('ascii', 'backslashreplace') + encoding = 'utf8' + s.encode(encoding) + errors = 'strict' + s.encode('utf8', errors) + s.encode('utf8', errors=errors) + s.encode(errors=errors) + s.encode(encoding=encoding, errors=errors) + s.encode('latin2') + [out] def f(s): s :: str - r0 :: bytes - r1 :: str - r2 :: bytes - r3, r4 :: str - r5 :: bytes + r0, r1, r2, r3, r4, r5 :: bytes + r6, r7 :: str + r8 :: bytes + r9, r10 :: str + r11 :: bytes + r12, encoding :: str + r13 :: bytes + r14, errors, r15 :: str + r16 :: bytes + r17, r18 :: str + r19 :: object + r20 :: str + r21 :: tuple + r22 :: dict + r23 :: object + r24 :: str + r25 :: object + r26 :: str + r27 :: tuple + r28 :: dict + r29 :: object + r30 :: str + r31 :: object + r32, r33 :: str + r34 :: tuple + r35 :: dict + r36 :: object + r37 :: str + r38 :: bytes L0: - r0 = CPy_Encode(s, 0, 0) - r1 = 'utf-8' - r2 = CPy_Encode(s, r1, 0) - r3 = 'ascii' - r4 = 'backslashreplace' - r5 = CPy_Encode(s, r3, r4) + r0 = PyUnicode_AsUTF8String(s) + r1 = PyUnicode_AsUTF8String(s) + r2 = PyUnicode_AsUTF8String(s) + r3 = PyUnicode_AsLatin1String(s) + r4 = PyUnicode_AsASCIIString(s) + r5 = PyUnicode_AsLatin1String(s) + r6 = 'utf-8' + r7 = 'backslashreplace' + r8 = CPy_Encode(s, r6, r7) + r9 = 'ascii' + r10 = 'backslashreplace' + r11 = CPy_Encode(s, r9, r10) + r12 = 'utf8' + encoding = r12 + r13 = CPy_Encode(s, encoding, 0) + r14 = 'strict' + errors = r14 + r15 = 'utf8' + r16 = CPy_Encode(s, r15, errors) + r17 = 'utf8' + r18 = 'encode' + r19 = CPyObject_GetAttr(s, r18) + r20 = 'errors' + r21 = PyTuple_Pack(1, r17) + r22 = CPyDict_Build(1, r20, errors) + r23 = PyObject_Call(r19, r21, r22) + r24 = 'encode' + r25 = CPyObject_GetAttr(s, r24) + r26 = 'errors' + r27 = PyTuple_Pack(0) + r28 = CPyDict_Build(1, r26, errors) + r29 = PyObject_Call(r25, r27, r28) + r30 = 'encode' + r31 = CPyObject_GetAttr(s, r30) + r32 = 'encoding' + r33 = 'errors' + r34 = PyTuple_Pack(0) + r35 = CPyDict_Build(2, r32, encoding, r33, errors) + r36 = PyObject_Call(r31, r34, r35) + r37 = 'latin2' + r38 = CPy_Encode(s, r37, 0) return 1 + +[case testOrd] +def str_ord(x: str) -> int: + return ord(x) +def str_ord_literal() -> int: + return ord("a") +def bytes_ord(x: bytes) -> int: + return ord(x) +def bytes_ord_literal() -> int: + return ord(b"a") +def any_ord(x) -> int: + return ord(x) +[out] +def str_ord(x): + x :: str + r0 :: int +L0: + r0 = CPyStr_Ord(x) + return r0 +def str_ord_literal(): +L0: + return 194 +def bytes_ord(x): + x :: bytes + r0 :: int +L0: + r0 = CPyBytes_Ord(x) + return r0 +def bytes_ord_literal(): +L0: + return 194 +def any_ord(x): + x, r0 :: object + r1 :: str + r2, r3 :: object + r4 :: int +L0: + r0 = builtins :: module + r1 = 'ord' + r2 = CPyObject_GetAttr(r0, r1) + r3 = PyObject_CallFunctionObjArgs(r2, x, 0) + r4 = unbox(int, r3) + return r4 diff --git a/mypyc/test-data/run-bytes.test b/mypyc/test-data/run-bytes.test index aaf541194ac65..fa63c46a67983 100644 --- a/mypyc/test-data/run-bytes.test +++ b/mypyc/test-data/run-bytes.test @@ -111,6 +111,29 @@ def test_len() -> None: assert len(b) == 3 assert len(bytes()) == 0 +def test_ord() -> None: + assert ord(b'a') == ord('a') + assert ord(b'a' + bytes()) == ord('a') + assert ord(b'\x00') == 0 + assert ord(b'\x00' + bytes()) == 0 + assert ord(b'\xfe') == 254 + assert ord(b'\xfe' + bytes()) == 254 + + with assertRaises(TypeError): + ord(b'aa') + with assertRaises(TypeError): + ord(b'') + +def test_ord_bytesarray() -> None: + assert ord(bytearray(b'a')) == ord('a') + assert ord(bytearray(b'\x00')) == 0 + assert ord(bytearray(b'\xfe')) == 254 + + with assertRaises(TypeError): + ord(bytearray(b'aa')) + with assertRaises(TypeError): + ord(bytearray(b'')) + [case testBytesSlicing] def test_bytes_slicing() -> None: b = b'abcdefg' diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 59617714f7e73..387f91e489ee4 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -3,8 +3,13 @@ class Empty: pass def f(e: Empty) -> Empty: return e + +class EmptyEllipsis: ... + +def g(e: EmptyEllipsis) -> EmptyEllipsis: + return e [file driver.py] -from native import Empty, f +from native import Empty, EmptyEllipsis, f, g print(isinstance(Empty, type)) print(Empty) @@ -12,11 +17,22 @@ print(str(Empty())[:20]) e = Empty() print(f(e) is e) + +print(isinstance(EmptyEllipsis, type)) +print(EmptyEllipsis) +print(str(EmptyEllipsis())[:28]) + +e2 = EmptyEllipsis() +print(g(e2) is e2) [out] True + int: @@ -438,6 +467,21 @@ a = A(10) assert a.foo() == 11 assert foo() == 21 +[case testClassKwargs] +class X: + def __init__(self, msg: str, **variables: int) -> None: + pass +[file driver.py] +import traceback +from native import X +X('hello', a=0) +try: + X('hello', msg='hello') +except TypeError as e: + print(f"{type(e).__name__}: {e}") +[out] +TypeError: argument for __init__() given by name ('msg') and position (1) + [case testGenericClass] from typing import TypeVar, Generic, Sequence T = TypeVar('T') @@ -2503,3 +2547,126 @@ class C: def test_final_attribute() -> None: assert C.A == -1 assert C.a == [-1] + +[case testClassWithFinalDecorator] +from typing import final + +@final +class C: + def a(self) -> int: + return 1 + +def test_class_final_attribute() -> None: + assert C().a() == 1 + + +[case testClassWithFinalDecoratorCtor] +from typing import final + +@final +class C: + def __init__(self) -> None: + self.a = 1 + + def b(self) -> int: + return 2 + + @property + def c(self) -> int: + return 3 + +def test_class_final_attribute() -> None: + assert C().a == 1 + assert C().b() == 2 + assert C().c == 3 + +[case testClassWithFinalDecoratorInheritedWithProperties] +from typing import final + +class B: + def a(self) -> int: + return 2 + + @property + def b(self) -> int: + return self.a() + 2 + + @property + def c(self) -> int: + return 3 + +def test_class_final_attribute_basic() -> None: + assert B().a() == 2 + assert B().b == 4 + assert B().c == 3 + +@final +class C(B): + def a(self) -> int: + return 1 + + @property + def b(self) -> int: + return self.a() + 1 + +def fn(cl: B) -> int: + return cl.a() + +def test_class_final_attribute_inherited() -> None: + assert C().a() == 1 + assert fn(C()) == 1 + assert B().a() == 2 + assert fn(B()) == 2 + + assert B().b == 4 + assert C().b == 2 + assert B().c == 3 + assert C().c == 3 + +[case testClassWithFinalAttributeAccess] +from typing import Final + +class C: + a: Final = {'x': 'y'} + b: Final = C.a + +def test_final_attribute() -> None: + assert C.a['x'] == 'y' + assert C.b['x'] == 'y' + assert C.a is C.b + +[case testClassDerivedFromIntEnum] +from enum import IntEnum, auto + +class Player(IntEnum): + MIN = auto() + +print(f'{Player.MIN = }') +[file driver.py] +from native import Player +[out] +Player.MIN = + +[case testStaticCallsWithUnpackingArgs] +from typing import Tuple + +class Foo: + @staticmethod + def static(a: int, b: int, c: int) -> Tuple[int, int, int]: + return (c+1, a+2, b+3) + + @classmethod + def clsmethod(cls, a: int, b: int, c: int) -> Tuple[int, int, int]: + return (c+1, a+2, b+3) + + +print(Foo.static(*[10, 20, 30])) +print(Foo.static(*(40, 50), *[60])) +assert Foo.static(70, 80, *[90]) == Foo.clsmethod(70, *(80, 90)) + +[file driver.py] +import native + +[out] +(31, 12, 23) +(61, 42, 53) diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test index 58b862e3f3032..d4f5b945309e5 100644 --- a/mypyc/test-data/run-dicts.test +++ b/mypyc/test-data/run-dicts.test @@ -157,7 +157,11 @@ else: try: clear_during_iter(d) except RuntimeError as e: - assert str(e) == "OrderedDict changed size during iteration" + assert str(e) in ( + "OrderedDict changed size during iteration", + # Error message changed in Python 3.13 and some 3.12 patch version + "OrderedDict mutated during iteration", + ) else: assert False diff --git a/mypyc/test-data/run-dunders-special.test b/mypyc/test-data/run-dunders-special.test new file mode 100644 index 0000000000000..cd02cca65eef7 --- /dev/null +++ b/mypyc/test-data/run-dunders-special.test @@ -0,0 +1,9 @@ +[case testDundersNotImplemented] +# This case is special because it tests the behavior of NotImplemented +# used in a typed function which return type is bool. +# This is a convention that can be overriden by the user. +class UsesNotImplemented: + def __eq__(self, b: object) -> bool: + return NotImplemented + +assert UsesNotImplemented() != object() diff --git a/mypyc/test-data/run-dunders.test b/mypyc/test-data/run-dunders.test index 2845187de2c31..b8fb13c9dcecb 100644 --- a/mypyc/test-data/run-dunders.test +++ b/mypyc/test-data/run-dunders.test @@ -32,10 +32,6 @@ class BoxedThing: class Subclass2(BoxedThing): pass -class UsesNotImplemented: - def __eq__(self, b: object) -> bool: - return NotImplemented - def index_into(x : Any, y : Any) -> Any: return x[y] @@ -81,8 +77,6 @@ assert is_truthy(Item('a')) assert not is_truthy(Subclass1('')) assert is_truthy(Subclass1('a')) -assert UsesNotImplemented() != object() - internal_index_into() [out] 7 7 @@ -943,3 +937,31 @@ def test_errors() -> None: pow(ForwardNotImplemented(), Child(), 3) # type: ignore with assertRaises(TypeError, "unsupported operand type(s) for ** or pow(): 'ForwardModRequired' and 'int'"): ForwardModRequired()**3 # type: ignore + +[case testDundersWithFinal] +from typing import final +class A: + def __init__(self, x: int) -> None: + self.x = x + + def __add__(self, y: int) -> int: + return self.x + y + + def __lt__(self, x: 'A') -> bool: + return self.x < x.x + +@final +class B(A): + def __add__(self, y: int) -> int: + return self.x + y + 1 + + def __lt__(self, x: 'A') -> bool: + return self.x < x.x + 1 + +def test_final() -> None: + a = A(5) + b = B(5) + assert a + 3 == 8 + assert b + 3 == 9 + assert (a < A(5)) is False + assert (b < A(5)) is True diff --git a/mypyc/test-data/run-exceptions.test b/mypyc/test-data/run-exceptions.test index c591fc1d8c158..1b180b9331971 100644 --- a/mypyc/test-data/run-exceptions.test +++ b/mypyc/test-data/run-exceptions.test @@ -80,6 +80,43 @@ Traceback (most recent call last): File "native.py", line 23, in __init__ raise Exception Exception +[out version>=3.13] +Traceback (most recent call last): + File "driver.py", line 4, in + f([]) + ~^^^^ + File "native.py", line 3, in f + g(x) + File "native.py", line 6, in g + x[5] = 2 +IndexError: list assignment index out of range +Traceback (most recent call last): + File "driver.py", line 8, in + r1() + ~~^^ + File "native.py", line 10, in r1 + q1() + File "native.py", line 13, in q1 + raise Exception("test") +Exception: test +Traceback (most recent call last): + File "driver.py", line 12, in + r2() + ~~^^ + File "native.py", line 16, in r2 + q2() + File "native.py", line 19, in q2 + raise Exception +Exception +Traceback (most recent call last): + File "driver.py", line 16, in + hey() + ~~~^^ + File "native.py", line 26, in hey + A() + File "native.py", line 23, in __init__ + raise Exception +Exception [case testTryExcept] from typing import Any, Iterator @@ -264,6 +301,55 @@ attr! -- 'object' object has no attribute 'lol' out! == l == key! -- 0 +[out version>=3.13] +== i == + +Traceback (most recent call last): + File "driver.py", line 6, in + i() + ~^^ + File "native.py", line 44, in i + r(0) + File "native.py", line 15, in r + [0][1] +IndexError: list index out of range +== k == +Traceback (most recent call last): + File "native.py", line 59, in k + r(1) + File "native.py", line 17, in r + raise Exception('hi') +Exception: hi + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "driver.py", line 12, in + k() + ~^^ + File "native.py", line 61, in k + r(0) + File "native.py", line 15, in r + [0][1] +IndexError: list index out of range +== g == +caught! +caught! +== f == +hi +None +list index out of range +None +== h == +gonna break +None +== j == +lookup! +lookup! +attr! -- 'object' object has no attribute 'lol' +out! +== l == +key! -- 0 [case testTryFinally] from typing import Any diff --git a/mypyc/test-data/run-i64.test b/mypyc/test-data/run-i64.test index 1a82ac3e2dd12..36567c949d794 100644 --- a/mypyc/test-data/run-i64.test +++ b/mypyc/test-data/run-i64.test @@ -1307,28 +1307,28 @@ def test_many_locals() -> None: a31: i64 = 31 a32: i64 = 32 a33: i64 = 33 - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a0) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a31) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a32) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a33) a0 = 5 assert a0 == 5 - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a31) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a32) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a33) a32 = 55 assert a0 == 5 assert a32 == 55 - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a31) - with assertRaises(NameError): + with assertRaises(UnboundLocalError): print(a33) a31 = 10 a33 = 20 diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 6f7d79059a6d5..76fbb06200a30 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -228,6 +228,7 @@ def nested_enumerate() -> None: assert i == inner inner += 1 outer += 1 + assert i == 2 assert outer_seen == l1 def nested_range() -> None: @@ -381,6 +382,41 @@ RuntimeError: dictionary changed size during iteration 1 2 3 +[out version>=3.13] +Traceback (most recent call last): + File "driver.py", line 16, in + iterate_over_any(5) + ~~~~~~~~~~~~~~~~^^^ + File "native.py", line 6, in iterate_over_any + for element in a: +TypeError: 'int' object is not iterable +Traceback (most recent call last): + File "driver.py", line 20, in + iterate_over_iterable(broken_generator(5)) + ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^ + File "native.py", line 10, in iterate_over_iterable + for element in iterable: + File "driver.py", line 8, in broken_generator + raise Exception('Exception Manually Raised') +Exception: Exception Manually Raised +Traceback (most recent call last): + File "driver.py", line 24, in + iterate_and_delete(d) + ~~~~~~~~~~~~~~~~~~^^^ + File "native.py", line 14, in iterate_and_delete + for key in d: +RuntimeError: dictionary changed size during iteration +15 +6 +3 +0 +1 +2 +3 +4 +1 +2 +3 [case testContinueFor] def f() -> None: @@ -430,6 +466,29 @@ assert g([6, 7], ['a', 'b']) == [(0, 6, 'a'), (1, 7, 'b')] assert f([6, 7], [8]) == [(0, 6, 8)] assert f([6], [8, 9]) == [(0, 6, 8)] +[case testEnumerateEmptyList] +from typing import List + +def get_enumerate_locals(iterable: List[int]) -> int: + for i, j in enumerate(iterable): + pass + try: + return i + except NameError: + return -100 + +[file driver.py] +from native import get_enumerate_locals + +print(get_enumerate_locals([])) +print(get_enumerate_locals([55])) +print(get_enumerate_locals([551, 552])) + +[out] +-100 +0 +1 + [case testIterTypeTrickiness] # Test inferring the type of a for loop body doesn't cause us grief # Extracted from somethings that broke in mypy diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test index 14bb5be979ae1..2252f3aa104a1 100644 --- a/mypyc/test-data/run-misc.test +++ b/mypyc/test-data/run-misc.test @@ -37,9 +37,9 @@ from testutil import assertRaises f(True, True) f(False, False) -with assertRaises(NameError): +with assertRaises(UnboundLocalError): f(False, True) -with assertRaises(NameError): +with assertRaises(UnboundLocalError): g() [out] lol @@ -968,7 +968,10 @@ print(z) [case testCheckVersion] import sys -if sys.version_info[:2] == (3, 12): +if sys.version_info[:2] == (3, 13): + def version() -> int: + return 13 +elif sys.version_info[:2] == (3, 12): def version() -> int: return 12 elif sys.version_info[:2] == (3, 11): diff --git a/mypyc/test-data/run-multimodule.test b/mypyc/test-data/run-multimodule.test index 70c73dc2088b6..5edd5688140ea 100644 --- a/mypyc/test-data/run-multimodule.test +++ b/mypyc/test-data/run-multimodule.test @@ -291,6 +291,23 @@ Traceback (most recent call last): File "other.py", line 3, in fail2 x[2] = 2 IndexError: list assignment index out of range +[out version>=3.13] +Traceback (most recent call last): + File "driver.py", line 6, in + other.fail2() + ~~~~~~~~~~~^^ + File "other.py", line 3, in fail2 + x[2] = 2 +IndexError: list assignment index out of range +Traceback (most recent call last): + File "driver.py", line 12, in + native.fail() + ~~~~~~~~~~~^^ + File "native.py", line 4, in fail + fail2() + File "other.py", line 3, in fail2 + x[2] = 2 +IndexError: list assignment index out of range [case testMultiModuleCycle] if False: diff --git a/mypyc/test-data/run-primitives.test b/mypyc/test-data/run-primitives.test index b95f742977be3..694700d4738ca 100644 --- a/mypyc/test-data/run-primitives.test +++ b/mypyc/test-data/run-primitives.test @@ -345,10 +345,10 @@ delAttribute() delAttributeMultiple() with assertRaises(AttributeError): native.global_var -with assertRaises(NameError, 'local variable "dummy" referenced before assignment'): +with assertRaises(UnboundLocalError, 'local variable "dummy" referenced before assignment'): delLocal(True) assert delLocal(False) == 10 -with assertRaises(NameError, 'local variable "dummy" referenced before assignment'): +with assertRaises(UnboundLocalError, 'local variable "dummy" referenced before assignment'): delLocalLoop() [out] (1, 2, 3) diff --git a/mypyc/test-data/run-python312.test b/mypyc/test-data/run-python312.test index 5e8a388fd8d37..a5a3f058d1e2f 100644 --- a/mypyc/test-data/run-python312.test +++ b/mypyc/test-data/run-python312.test @@ -1,5 +1,4 @@ [case testPEP695Basics] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Any, TypeAliasType, cast from testutil import assertRaises @@ -192,7 +191,6 @@ def test_recursive_type_alias() -> None: [typing fixtures/typing-full.pyi] [case testPEP695GenericTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable from types import GenericAlias diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index be668435d073f..1caddce9848d9 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -565,25 +565,32 @@ def test_chr() -> None: assert try_invalid(1114112) [case testOrd] +from testutil import assertRaises + def test_ord() -> None: + assert ord(' ') == 32 + assert ord(' ' + str()) == 32 + assert ord('\x00') == 0 + assert ord('\x00' + str()) == 0 assert ord('\ue000') == 57344 - s = "a\xac\u1234\u20ac\U00008000" - # ^^^^ two-digit hex escape - # ^^^^^^ four-digit Unicode escape - # ^^^^^^^^^^ eight-digit Unicode escape + assert ord('\ue000' + str()) == 57344 + s = "a\xac\u1234\u20ac\U00010000" + # ^^^^ two-digit hex escape + # ^^^^^^ four-digit Unicode escape + # ^^^^^^^^^^ eight-digit Unicode escape l1 = [ord(c) for c in s] - assert l1 == [97, 172, 4660, 8364, 32768] + assert l1 == [97, 172, 4660, 8364, 65536] u = 'abcdé' assert ord(u[-1]) == 233 assert ord(b'a') == 97 assert ord(b'a' + bytes()) == 97 - u2 = '\U0010ffff' + u2 = '\U0010ffff' + str() assert ord(u2) == 1114111 - try: + assert ord('\U0010ffff') == 1114111 + with assertRaises(TypeError, "ord() expected a character, but a string of length 2 found"): ord('aa') - assert False - except TypeError: - pass + with assertRaises(TypeError): + ord('') [case testDecode] def test_decode() -> None: @@ -639,3 +646,12 @@ def test_encode() -> None: assert u'\u00E1'.encode('latin1') == b'\xe1' with assertRaises(UnicodeEncodeError): u.encode('latin1') + +[case testUnicodeSurrogate] +def f() -> str: + return "\ud800" + +def test_surrogate() -> None: + assert ord(f()) == 0xd800 + assert ord("\udfff") == 0xdfff + assert repr("foobar\x00\xab\ud912\U00012345") == r"'foobar\x00«\ud912𒍅'" diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index f2af41c22ea96..7ab055c735ad5 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -9,7 +9,6 @@ from mypyc.ir.ops import PrimitiveDescription from mypyc.primitives import registry -from mypyc.primitives.registry import CFunctionDescription class TestHeaderInclusion(unittest.TestCase): @@ -26,18 +25,12 @@ def check_name(name: str) -> None: rf"\b{name}\b", header ), f'"{name}" is used in mypyc.primitives but not declared in CPy.h' - for old_values in [ + for values in [ registry.method_call_ops.values(), - registry.function_ops.values(), + registry.binary_ops.values(), registry.unary_ops.values(), + registry.function_ops.values(), ]: - for old_ops in old_values: - if isinstance(old_ops, CFunctionDescription): - old_ops = [old_ops] - for old_op in old_ops: - check_name(old_op.c_function_name) - - for values in [registry.binary_ops.values()]: for ops in values: if isinstance(ops, PrimitiveDescription): ops = [ops] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 317427afac5a7..90df131288f98 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -134,7 +134,7 @@ def test_integer(self) -> None: def test_tuple_get(self) -> None: self.assert_emit(TupleGet(self.t, 1, 0), "cpy_r_r0 = cpy_r_t.f1;") - def test_load_None(self) -> None: + def test_load_None(self) -> None: # noqa: N802 self.assert_emit( LoadAddress(none_object_op.type, none_object_op.src, 0), "cpy_r_r0 = (PyObject *)&_Py_NoneStruct;", @@ -154,6 +154,7 @@ def test_int_sub(self) -> None: ) def test_int_neg(self) -> None: + assert int_neg_op.c_function_name is not None self.assert_emit( CallC( int_neg_op.c_function_name, @@ -302,7 +303,7 @@ def test_dec_ref_tuple_nested(self) -> None: def test_list_get_item(self) -> None: self.assert_emit( CallC( - list_get_item_op.c_function_name, + str(list_get_item_op.c_function_name), [self.m, self.k], list_get_item_op.return_type, list_get_item_op.steals, @@ -316,7 +317,7 @@ def test_list_get_item(self) -> None: def test_list_set_item(self) -> None: self.assert_emit( CallC( - list_set_item_op.c_function_name, + str(list_set_item_op.c_function_name), [self.l, self.n, self.o], list_set_item_op.return_type, list_set_item_op.steals, @@ -352,7 +353,7 @@ def test_unbox_i64(self) -> None: def test_list_append(self) -> None: self.assert_emit( CallC( - list_append_op.c_function_name, + str(list_append_op.c_function_name), [self.l, self.o], list_append_op.return_type, list_append_op.steals, @@ -492,7 +493,7 @@ def test_set_attr_init_with_bitmap(self) -> None: def test_dict_get_item(self) -> None: self.assert_emit( CallC( - dict_get_item_op.c_function_name, + str(dict_get_item_op.c_function_name), [self.d, self.o2], dict_get_item_op.return_type, dict_get_item_op.steals, @@ -506,7 +507,7 @@ def test_dict_get_item(self) -> None: def test_dict_set_item(self) -> None: self.assert_emit( CallC( - dict_set_item_op.c_function_name, + str(dict_set_item_op.c_function_name), [self.d, self.o, self.o2], dict_set_item_op.return_type, dict_set_item_op.steals, @@ -520,7 +521,7 @@ def test_dict_set_item(self) -> None: def test_dict_update(self) -> None: self.assert_emit( CallC( - dict_update_op.c_function_name, + str(dict_update_op.c_function_name), [self.d, self.o], dict_update_op.return_type, dict_update_op.steals, diff --git a/mypyc/test/test_external.py b/mypyc/test/test_external.py index 22eb8019133c8..010c74dee42e5 100644 --- a/mypyc/test/test_external.py +++ b/mypyc/test/test_external.py @@ -20,7 +20,9 @@ def test_c_unit_test(self) -> None: cppflags: list[str] = [] env = os.environ.copy() if sys.platform == "darwin": - cppflags += ["-mmacosx-version-min=10.10", "-stdlib=libc++"] + cppflags += ["-O0", "-mmacosx-version-min=10.10", "-stdlib=libc++"] + elif sys.platform == "linux": + cppflags += ["-O0"] env["CPPFLAGS"] = " ".join(cppflags) # Build Python wrapper for C unit tests. diff --git a/mypyc/test/test_namegen.py b/mypyc/test/test_namegen.py index 509018b4c3bde..f88edbd00dce9 100644 --- a/mypyc/test/test_namegen.py +++ b/mypyc/test/test_namegen.py @@ -35,6 +35,12 @@ def test_make_module_translation_map(self) -> None: "fu.bar": "fu.bar.", "foo.baz": "baz.", } + assert make_module_translation_map(["foo", "foo.foo", "bar.foo", "bar.foo.bar.foo"]) == { + "foo": "foo.", + "foo.foo": "foo.foo.", + "bar.foo": "bar.foo.", + "bar.foo.bar.foo": "foo.bar.foo.", + } def test_name_generator(self) -> None: g = NameGenerator([["foo", "foo.zar"]]) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 37de192a92913..dd3c79da7b9b2 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -63,6 +63,7 @@ "run-bench.test", "run-mypy-sim.test", "run-dunders.test", + "run-dunders-special.test", "run-singledispatch.test", "run-attrs.test", "run-python37.test", @@ -140,6 +141,7 @@ class TestRun(MypycDataSuite): optional_out = True multi_file = False separate = False # If True, using separate (incremental) compilation + strict_dunder_typing = False def run_case(self, testcase: DataDrivenTestCase) -> None: # setup.py wants to be run from the root directory of the package, which we accommodate @@ -196,7 +198,6 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> options.preserve_asts = True options.allow_empty_bodies = True options.incremental = self.separate - options.enable_incomplete_feature.append("NewGenericSyntax") # Avoid checking modules/packages named 'unchecked', to provide a way # to test interacting with code we don't have types for. @@ -233,7 +234,11 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> groups = construct_groups(sources, separate, len(module_names) > 1) try: - compiler_options = CompilerOptions(multi_file=self.multi_file, separate=self.separate) + compiler_options = CompilerOptions( + multi_file=self.multi_file, + separate=self.separate, + strict_dunder_typing=self.strict_dunder_typing, + ) result = emitmodule.parse_and_typecheck( sources=sources, options=options, @@ -402,6 +407,14 @@ class TestRunSeparate(TestRun): files = ["run-multimodule.test", "run-mypy-sim.test"] +class TestRunStrictDunderTyping(TestRun): + """Run the tests with strict dunder typing.""" + + strict_dunder_typing = True + test_name_suffix = "_dunder_typing" + files = ["run-dunders.test", "run-floats.test"] + + def fix_native_line_number(message: str, fnam: str, delta: int) -> str: """Update code locations in test case output to point to the .test file. diff --git a/pyproject.toml b/pyproject.toml index 12a0dc109cd53..1a7adf21c0a6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,8 +3,7 @@ requires = [ # NOTE: this needs to be kept in sync with mypy-requirements.txt # and build-requirements.txt, because those are both needed for # self-typechecking :/ - "setuptools >= 40.6.2", - "wheel >= 0.30.0", + "setuptools >= 75.1.0", # the following is from mypy-requirements.txt/setup.py "typing_extensions>=4.6.0", "mypy_extensions>=1.0.0", @@ -15,6 +14,83 @@ requires = [ ] build-backend = "setuptools.build_meta" +[project] +name = "mypy" +description = "Optional static typing for Python" +readme = {text = """ +Mypy -- Optional Static Typing for Python +========================================= + +Add type annotations to your Python programs, and use mypy to type +check them. Mypy is essentially a Python linter on steroids, and it +can catch many programming errors by analyzing your program, without +actually having to run it. Mypy has a powerful type system with +features such as type inference, gradual typing, generics and union +types. +""", content-type = "text/x-rst"} +authors = [{name = "Jukka Lehtosalo", email = "jukka.lehtosalo@iki.fi"}] +license = {text = "MIT"} +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development", + "Typing :: Typed", +] +requires-python = ">=3.8" +dependencies = [ + # When changing this, also update build-system.requires and mypy-requirements.txt + "typing_extensions>=4.6.0", + "mypy_extensions>=1.0.0", + "tomli>=1.1.0; python_version<'3.11'", +] +dynamic = ["version"] + +[project.optional-dependencies] +dmypy = ["psutil>=4.0"] +mypyc = ["setuptools>=50"] +python2 = [] +reports = ["lxml"] +install-types = ["pip"] +faster-cache = ["orjson"] + +[project.urls] +Homepage = "https://www.mypy-lang.org/" +Documentation = "https://mypy.readthedocs.io/en/stable/index.html" +Repository = "https://github.com/python/mypy" +Changelog = "https://github.com/python/mypy/blob/master/CHANGELOG.md" +Issues = "https://github.com/python/mypy/issues" + +[project.scripts] +mypy = "mypy.__main__:console_entry" +stubgen = "mypy.stubgen:main" +stubtest = "mypy.stubtest:main" +dmypy = "mypy.dmypy.client:console_entry" +mypyc = "mypyc.__main__:main" + +[tool.setuptools.packages.find] +include = ["mypy*", "mypyc*", "*__mypyc*"] +namespaces = false + +[tool.setuptools.package-data] +mypy = [ + "py.typed", + "typeshed/**/*.py", + "typeshed/**/*.pyi", + "typeshed/stdlib/VERSIONS", + "xml/*.xsd", + "xml/*.xslt", + "xml/*.css", +] + [tool.black] line-length = 99 target-version = ["py38", "py39", "py310", "py311", "py312"] @@ -50,6 +126,7 @@ select = [ "W", # pycodestyle (warning) "B", # flake8-bugbear "I", # isort + "N", # pep8-naming "RUF100", # Unused noqa comments "PGH004", # blanket noqa comments "UP", # pyupgrade @@ -66,8 +143,12 @@ ignore = [ "E2", # conflicts with black "E402", # module level import not at top of file "E501", # conflicts with black + "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for isinstance checks "E731", # Do not assign a `lambda` expression, use a `def` "E741", # Ambiguous variable name + "N818", # Exception should be named with an Error suffix + "N806", # UPPER_CASE used for constant local variables + "UP031", # Use format specifiers instead of percent format "UP032", # 'f-string always preferable to format' is controversial "C416", # There are a few cases where it's nice to have names for the dict items ] @@ -80,6 +161,10 @@ unfixable = [ "UP036", # sometimes it's better to just noqa this ] +[tool.ruff.lint.per-file-ignores] +# Mixed case variable and function names. +"mypy/fastparse.py" = ["N802", "N816"] + [tool.ruff.lint.isort] combine-as-imports = true extra-standard-library = ["typing_extensions"] diff --git a/runtests.py b/runtests.py index 80ef8d814ee1e..9863e84915004 100755 --- a/runtests.py +++ b/runtests.py @@ -17,6 +17,8 @@ MYPYC_RUN_MULTI = "TestRunMultiFile" MYPYC_EXTERNAL = "TestExternal" MYPYC_COMMAND_LINE = "TestCommandLine" +MYPYC_SEPARATE = "TestRunSeparate" +MYPYC_MULTIMODULE = "multimodule" # Subset of mypyc run tests that are slow ERROR_STREAM = "ErrorStreamSuite" @@ -31,6 +33,7 @@ MYPYC_RUN_MULTI, MYPYC_EXTERNAL, MYPYC_COMMAND_LINE, + MYPYC_SEPARATE, ERROR_STREAM, ] @@ -40,7 +43,10 @@ # These must be enabled by explicitly including 'mypyc-extra' on the command line. -MYPYC_OPT_IN = [MYPYC_RUN, MYPYC_RUN_MULTI] +MYPYC_OPT_IN = [MYPYC_RUN, MYPYC_RUN_MULTI, MYPYC_SEPARATE] + +# These mypyc test filters cover most slow test cases +MYPYC_SLOW = [MYPYC_RUN_MULTI, MYPYC_COMMAND_LINE, MYPYC_SEPARATE, MYPYC_MULTIMODULE] # We split the pytest run into three parts to improve test @@ -77,6 +83,7 @@ "-k", " or ".join([DAEMON, MYPYC_EXTERNAL, MYPYC_COMMAND_LINE, ERROR_STREAM]), ], + "mypyc-fast": ["pytest", "-q", "mypyc", "-k", f"not ({' or '.join(MYPYC_SLOW)})"], # Test cases that might take minutes to run "pytest-extra": ["pytest", "-q", "-k", " or ".join(PYTEST_OPT_IN)], # Mypyc tests that aren't run by default, since they are slow and rarely @@ -87,7 +94,7 @@ # Stop run immediately if these commands fail FAST_FAIL = ["self", "lint"] -EXTRA_COMMANDS = ("pytest-extra", "mypyc-extra") +EXTRA_COMMANDS = ("pytest-extra", "mypyc-fast", "mypyc-extra") DEFAULT_COMMANDS = [cmd for cmd in cmds if cmd not in EXTRA_COMMANDS] assert all(cmd in cmds for cmd in FAST_FAIL) diff --git a/setup.py b/setup.py index 160e2b054b0ec..180faf6d8ded8 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ # This requires setuptools when building; setuptools is not needed # when installing from a wheel file (though it is still needed for # alternative forms of installing, as suggested by README.md). -from setuptools import Extension, find_packages, setup +from setuptools import Extension, setup from setuptools.command.build_py import build_py from mypy.version import __version__ as version @@ -26,19 +26,6 @@ if TYPE_CHECKING: from typing_extensions import TypeGuard -description = "Optional static typing for Python" -long_description = """ -Mypy -- Optional Static Typing for Python -========================================= - -Add type annotations to your Python programs, and use mypy to type -check them. Mypy is essentially a Python linter on steroids, and it -can catch many programming errors by analyzing your program, without -actually having to run it. Mypy has a powerful type system with -features such as type inference, gradual typing, generics and union -types. -""".lstrip() - def is_list_of_setuptools_extension(items: list[Any]) -> TypeGuard[list[Extension]]: return all(isinstance(item, Extension) for item in items) @@ -78,13 +65,6 @@ def run(self): cmdclass = {"build_py": CustomPythonBuild} -package_data = ["py.typed"] - -package_data += find_package_data(os.path.join("mypy", "typeshed"), ["*.py", "*.pyi"]) -package_data += [os.path.join("mypy", "typeshed", "stdlib", "VERSIONS")] - -package_data += find_package_data(os.path.join("mypy", "xml"), ["*.xsd", "*.xslt", "*.css"]) - USE_MYPYC = False # To compile with mypyc, a mypyc checkout must be present on the PYTHONPATH if len(sys.argv) > 1 and "--use-mypyc" in sys.argv: @@ -179,65 +159,4 @@ def run(self): ext_modules = [] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Software Development", - "Typing :: Typed", -] - -setup( - name="mypy", - version=version, - description=description, - long_description=long_description, - author="Jukka Lehtosalo", - author_email="jukka.lehtosalo@iki.fi", - url="https://www.mypy-lang.org/", - license="MIT", - py_modules=[], - ext_modules=ext_modules, - packages=find_packages(), - package_data={"mypy": package_data}, - entry_points={ - "console_scripts": [ - "mypy=mypy.__main__:console_entry", - "stubgen=mypy.stubgen:main", - "stubtest=mypy.stubtest:main", - "dmypy=mypy.dmypy.client:console_entry", - "mypyc=mypyc.__main__:main", - ] - }, - classifiers=classifiers, - cmdclass=cmdclass, - # When changing this, also update mypy-requirements.txt and pyproject.toml - install_requires=[ - "typing_extensions>=4.6.0", - "mypy_extensions >= 1.0.0", - "tomli>=1.1.0; python_version<'3.11'", - ], - # Same here. - extras_require={ - "dmypy": "psutil >= 4.0", - "mypyc": "setuptools >= 50", - "python2": "", - "reports": "lxml", - "install-types": "pip", - }, - python_requires=">=3.8", - include_package_data=True, - project_urls={ - "Documentation": "https://mypy.readthedocs.io/en/stable/index.html", - "Repository": "https://github.com/python/mypy", - "Changelog": "https://github.com/python/mypy/blob/master/CHANGELOG.md", - "Issues": "https://github.com/python/mypy/issues", - }, -) +setup(version=version, ext_modules=ext_modules, cmdclass=cmdclass) diff --git a/test-data/unit/README.md b/test-data/unit/README.md index 5a9416603541d..aaf774d1b62fa 100644 --- a/test-data/unit/README.md +++ b/test-data/unit/README.md @@ -100,6 +100,10 @@ First install any additional dependencies needed for testing: python3 -m pip install -U -r test-requirements.txt +Configure `pre-commit` to run the linters automatically when you commit: + + pre-commit install + The unit test suites are driven by the `pytest` framework. To run all mypy tests, run `pytest` in the mypy repository: @@ -157,9 +161,11 @@ To run mypy on itself: python3 -m mypy --config-file mypy_self_check.ini -p mypy -To run the linter: +To run the linter (this commands just wraps `pre-commit`, so you can also +invoke it directly like `pre-commit run -a`, and this will also run when you +`git commit` if enabled): - ruff . + python3 runtests.py lint You can also run all of the above tests using `runtests.py` (this includes type checking mypy and linting): diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 876fe0c6be15a..0ef08e5a07751 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -573,6 +573,25 @@ async def return_f() -> AsyncGenerator[int, None]: [builtins fixtures/dict.pyi] [typing fixtures/typing-async.pyi] +[case testImplicitAsyncGenerator] +from typing import List + +async def get_list() -> List[int]: + return [1] + +async def predicate() -> bool: + return True + +async def test_implicit_generators() -> None: + reveal_type(await predicate() for _ in [1]) # N: Revealed type is "typing.AsyncGenerator[builtins.bool, None]" + reveal_type(x for x in [1] if await predicate()) # N: Revealed type is "typing.AsyncGenerator[builtins.int, None]" + reveal_type(x for x in await get_list()) # N: Revealed type is "typing.Generator[builtins.int, None, None]" + reveal_type(x for _ in [1] for x in await get_list()) # N: Revealed type is "typing.AsyncGenerator[builtins.int, None]" + +[builtins fixtures/dict.pyi] +[typing fixtures/typing-async.pyi] + + -- The full matrix of coroutine compatibility -- ------------------------------------------ diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 82208d27df41a..5ce80faaee184 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -147,7 +147,6 @@ class Base: class Derived(Base): __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]") - [case testOverridePartialAttributeWithMethod] # This was crashing: https://github.com/python/mypy/issues/11686. class Base: @@ -710,6 +709,123 @@ class B(A): @dec def f(self) -> Y: pass +[case testOverrideCallableAttributeWithMethod] +from typing import Callable + +class A: + f1: Callable[[str], None] + f2: Callable[[str], None] + f3: Callable[[str], None] + +class B(A): + def f1(self, x: object) -> None: + pass + + @classmethod + def f2(cls, x: object) -> None: + pass + + @staticmethod + def f3(x: object) -> None: + pass +[builtins fixtures/classmethod.pyi] + +[case testOverrideCallableAttributeWithMethodMutableOverride] +# flags: --enable-error-code=mutable-override +from typing import Callable + +class A: + f1: Callable[[str], None] + f2: Callable[[str], None] + f3: Callable[[str], None] + +class B(A): + def f1(self, x: object) -> None: pass # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") + + @classmethod + def f2(cls, x: object) -> None: pass # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") + + @staticmethod + def f3(x: object) -> None: pass # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") +[builtins fixtures/classmethod.pyi] + +[case testOverrideCallableAttributeWithSettableProperty] +from typing import Callable + +class A: + f: Callable[[str], None] + +class B(A): + @property + def f(self) -> Callable[[object], None]: pass + @func.setter + def f(self, x: object) -> None: pass +[builtins fixtures/property.pyi] + +[case testOverrideCallableAttributeWithSettablePropertyMutableOverride] +# flags: --enable-error-code=mutable-override +from typing import Callable + +class A: + f: Callable[[str], None] + +class B(A): + @property # E: Covariant override of a mutable attribute (base class "A" defined the type as "Callable[[str], None]", override has type "Callable[[object], None]") + def f(self) -> Callable[[object], None]: pass + @func.setter + def f(self, x: object) -> None: pass +[builtins fixtures/property.pyi] + +[case testOverrideCallableUnionAttributeWithMethod] +from typing import Callable, Union + +class A: + f1: Union[Callable[[str], str], str] + f2: Union[Callable[[str], str], str] + f3: Union[Callable[[str], str], str] + f4: Union[Callable[[str], str], str] + +class B(A): + def f1(self, x: str) -> str: + pass + + def f2(self, x: object) -> str: + pass + + @classmethod + def f3(cls, x: str) -> str: + pass + + @staticmethod + def f4(x: str) -> str: + pass +[builtins fixtures/classmethod.pyi] + +[case testOverrideCallableUnionAttributeWithMethodMutableOverride] +# flags: --enable-error-code=mutable-override +from typing import Callable, Union + +class A: + f1: Union[Callable[[str], str], str] + f2: Union[Callable[[str], str], str] + f3: Union[Callable[[str], str], str] + f4: Union[Callable[[str], str], str] + +class B(A): + def f1(self, x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]") + pass + + def f2(self, x: object) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[object], str]") + pass + + @classmethod + def f3(cls, x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]") + pass + + @staticmethod + def f4(x: str) -> str: # E: Covariant override of a mutable attribute (base class "A" defined the type as "Union[Callable[[str], str], str]", override has type "Callable[[str], str]") + pass +[builtins fixtures/classmethod.pyi] -- Constructors -- ------------ diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 2b3b3f4a86958..1e06f300570eb 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -232,6 +232,8 @@ reveal_type(magic.non_magic_method()) # N: Revealed type is "builtins.int" reveal_type(magic.non_magic_field) # N: Revealed type is "builtins.int" magic.nonexistent_field # E: Field does not exist reveal_type(magic.fallback_example) # N: Revealed type is "Any" +reveal_type(magic.no_assignment_field) # N: Revealed type is "builtins.float" +magic.no_assignment_field = "bad" # E: Cannot assign to field derived = DerivedMagic() reveal_type(derived.magic_field) # N: Revealed type is "builtins.str" @@ -250,6 +252,7 @@ class Magic: def __getattr__(self, x: Any) -> Any: ... def non_magic_method(self) -> int: ... non_magic_field: int + no_assignment_field: float class DerivedMagic(Magic): ... [file mypy.ini] @@ -964,6 +967,7 @@ class Cls(enum.Enum): attr = 'test' reveal_type(Cls.attr) # N: Revealed type is "builtins.int" +[builtins fixtures/enum.pyi] [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/class_attr_hook.py diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index f26ccd9a48541..6de428109c727 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2489,3 +2489,54 @@ class Base: class Child(Base): y: int [builtins fixtures/dataclasses.pyi] + +[case testDunderReplacePresent] +# flags: --python-version 3.13 +from dataclasses import dataclass, field + +@dataclass +class Coords: + x: int + y: int + # non-init fields are not allowed with replace: + z: int = field(init=False) + + +replaced = Coords(2, 4).__replace__(x=2, y=5) +reveal_type(replaced) # N: Revealed type is "__main__.Coords" + +replaced = Coords(2, 4).__replace__(x=2) +reveal_type(replaced) # N: Revealed type is "__main__.Coords" + +Coords(2, 4).__replace__(x="asdf") # E: Argument "x" to "__replace__" of "Coords" has incompatible type "str"; expected "int" +Coords(2, 4).__replace__(23) # E: Too many positional arguments for "__replace__" of "Coords" +Coords(2, 4).__replace__(23, 25) # E: Too many positional arguments for "__replace__" of "Coords" +Coords(2, 4).__replace__(x=23, y=25, z=42) # E: Unexpected keyword argument "z" for "__replace__" of "Coords" + +from typing import Generic, TypeVar +T = TypeVar('T') + +@dataclass +class Gen(Generic[T]): + x: T + +replaced_2 = Gen(2).__replace__(x=2) +reveal_type(replaced_2) # N: Revealed type is "__main__.Gen[builtins.int]" +Gen(2).__replace__(x="not an int") # E: Argument "x" to "__replace__" of "Gen" has incompatible type "str"; expected "int" + +[builtins fixtures/tuple.pyi] + +[case testDunderReplaceCovariantOverride] +# flags: --python-version 3.13 +from dataclasses import dataclass + +@dataclass +class Base: + a: object + +@dataclass +class Child(Base): # E: Argument 1 of "__replace__" is incompatible with supertype "Base"; supertype defines the argument type as "object" \ + # N: This violates the Liskov substitution principle \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides + a: int +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test new file mode 100644 index 0000000000000..362d8725f1831 --- /dev/null +++ b/test-data/unit/check-deprecated.test @@ -0,0 +1,636 @@ +-- Type checker test cases for reporting deprecations. + + +[case testDeprecatedDisabled] + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f() + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedAsNoteWithErrorCode] +# flags: --enable-error-code=deprecated --show-error-codes --report-deprecated-as-note + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f() # type: ignore[deprecated] +f() # N: function __main__.f is deprecated: use f2 instead [deprecated] + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedAsErrorWithErrorCode] +# flags: --enable-error-code=deprecated --show-error-codes + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f() # type: ignore[deprecated] +f() # E: function __main__.f is deprecated: use f2 instead [deprecated] + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedFunction] +# flags: --enable-error-code=deprecated + +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +f # E: function __main__.f is deprecated: use f2 instead # type: ignore[deprecated] +f(1) # E: function __main__.f is deprecated: use f2 instead \ + # E: Too many arguments for "f" +f[1] # E: function __main__.f is deprecated: use f2 instead \ + # E: Value of type "Callable[[], None]" is not indexable +g = f # E: function __main__.f is deprecated: use f2 instead +g() +t = (f, f, g) # E: function __main__.f is deprecated: use f2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedFunctionDifferentModule] +# flags: --enable-error-code=deprecated + +import m +import p.s +import m as n +import p.s as ps +from m import f # E: function m.f is deprecated: use f2 instead +from p.s import g # E: function p.s.g is deprecated: use g2 instead +from k import * + +m.f() # E: function m.f is deprecated: use f2 instead +p.s.g() # E: function p.s.g is deprecated: use g2 instead +n.f() # E: function m.f is deprecated: use f2 instead +ps.g() # E: function p.s.g is deprecated: use g2 instead +f() +g() +h() # E: function k.h is deprecated: use h2 instead + +[file m.py] +from typing_extensions import deprecated + +@deprecated("use f2 instead") +def f() -> None: ... + +[file p/s.py] +from typing_extensions import deprecated + +@deprecated("use g2 instead") +def g() -> None: ... + +[file k.py] +from typing_extensions import deprecated + +@deprecated("use h2 instead") +def h() -> None: ... + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClass] +# flags: --enable-error-code=deprecated + +from typing import Callable, List, Optional, Tuple, Union +from typing_extensions import deprecated, TypeAlias, TypeVar + +@deprecated("use C2 instead") +class C: ... + +c: C # E: class __main__.C is deprecated: use C2 instead +C() # E: class __main__.C is deprecated: use C2 instead +C.missing() # E: class __main__.C is deprecated: use C2 instead \ + # E: "Type[C]" has no attribute "missing" +C.__init__(c) # E: class __main__.C is deprecated: use C2 instead +C(1) # E: class __main__.C is deprecated: use C2 instead \ + # E: Too many arguments for "C" + +D = C # E: class __main__.C is deprecated: use C2 instead +D() +t = (C, C, D) # E: class __main__.C is deprecated: use C2 instead + +u1: Union[C, int] = 1 # E: class __main__.C is deprecated: use C2 instead +u1 = 1 +u2 = 1 # type: Union[C, int] # E: class __main__.C is deprecated: use C2 instead +u2 = 1 + +c1 = c2 = C() # E: class __main__.C is deprecated: use C2 instead +i, c3 = 1, C() # E: class __main__.C is deprecated: use C2 instead + +class E: ... + +x1: Optional[C] # E: class __main__.C is deprecated: use C2 instead +x2: Union[D, C, E] # E: class __main__.C is deprecated: use C2 instead +x3: Union[D, Optional[C], E] # E: class __main__.C is deprecated: use C2 instead +x4: Tuple[D, C, E] # E: class __main__.C is deprecated: use C2 instead +x5: Tuple[Tuple[D, C], E] # E: class __main__.C is deprecated: use C2 instead +x6: List[C] # E: class __main__.C is deprecated: use C2 instead +x7: List[List[C]] # E: class __main__.C is deprecated: use C2 instead +x8: List[Optional[Tuple[Union[List[C], int]]]] # E: class __main__.C is deprecated: use C2 instead +x9: Callable[[int], C] # E: class __main__.C is deprecated: use C2 instead +x10: Callable[[int, C, int], int] # E: class __main__.C is deprecated: use C2 instead + +T = TypeVar("T") +A1: TypeAlias = Optional[C] # E: class __main__.C is deprecated: use C2 instead +x11: A1 +A2: TypeAlias = List[Union[A2, C]] # E: class __main__.C is deprecated: use C2 instead +x12: A2 +A3: TypeAlias = List[Optional[T]] +x13: A3[C] # E: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedBaseClass] +# flags: --enable-error-code=deprecated + +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +class D(C): ... # E: class __main__.C is deprecated: use C2 instead +class E(D): ... +class F(D, C): ... # E: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassInTypeVar] +# flags: --enable-error-code=deprecated + +from typing import Generic, TypeVar +from typing_extensions import deprecated + +class B: ... +@deprecated("use C2 instead") +class C: ... + +T = TypeVar("T", bound=C) # E: class __main__.C is deprecated: use C2 instead +def f(x: T) -> T: ... +class D(Generic[T]): ... + +V = TypeVar("V", B, C) # E: class __main__.C is deprecated: use C2 instead +def g(x: V) -> V: ... +class E(Generic[V]): ... + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassInCast] +# flags: --enable-error-code=deprecated + +from typing import cast, Generic +from typing_extensions import deprecated + +class B: ... +@deprecated("use C2 instead") +class C: ... + +c = C() # E: class __main__.C is deprecated: use C2 instead +b = cast(B, c) + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedInstanceInFunctionDefinition] +# flags: --enable-error-code=deprecated + +from typing import Generic, List, Optional, TypeVar +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +def f1(c: C) -> None: # E: class __main__.C is deprecated: use C2 instead + def g1() -> None: ... + +def f2(c: List[Optional[C]]) -> None: # E: class __main__.C is deprecated: use C2 instead + def g2() -> None: ... + +def f3() -> C: # E: class __main__.C is deprecated: use C2 instead + def g3() -> None: ... + return C() # E: class __main__.C is deprecated: use C2 instead + +def f4() -> List[Optional[C]]: # E: class __main__.C is deprecated: use C2 instead + def g4() -> None: ... + return [] + +def f5() -> None: + def g5(c: C) -> None: ... # E: class __main__.C is deprecated: use C2 instead + +def f6() -> None: + def g6() -> C: ... # E: class __main__.C is deprecated: use C2 instead + + +@deprecated("use D2 instead") +class D: + + def f1(self, c: C) -> None: # E: class __main__.C is deprecated: use C2 instead + def g1() -> None: ... + + def f2(self, c: List[Optional[C]]) -> None: # E: class __main__.C is deprecated: use C2 instead + def g2() -> None: ... + + def f3(self) -> None: + def g3(c: C) -> None: ... # E: class __main__.C is deprecated: use C2 instead + + def f4(self) -> None: + def g4() -> C: ... # E: class __main__.C is deprecated: use C2 instead + +T = TypeVar("T") + +@deprecated("use E2 instead") +class E(Generic[T]): + + def f1(self: E[C]) -> None: ... # E: class __main__.C is deprecated: use C2 instead + def f2(self, e: E[C]) -> None: ... # E: class __main__.C is deprecated: use C2 instead + def f3(self) -> E[C]: ... # E: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassDifferentModule] +# flags: --enable-error-code=deprecated + +import m +import p.s +import m as n +import p.s as ps +from m import B, C # E: class m.B is deprecated: use B2 instead \ + # E: class m.C is deprecated: use C2 instead +from p.s import D # E: class p.s.D is deprecated: use D2 instead +from k import * + +m.C() # E: class m.C is deprecated: use C2 instead +p.s.D() # E: class p.s.D is deprecated: use D2 instead +n.C() # E: class m.C is deprecated: use C2 instead +ps.D() # E: class p.s.D is deprecated: use D2 instead +C() +D() +E() # E: class k.E is deprecated: use E2 instead + +x1: m.A # E: class m.A is deprecated: use A2 instead +x2: m.A = m.A() # E: class m.A is deprecated: use A2 instead +y1: B +y2: B = B() + +[file m.py] +from typing_extensions import deprecated + +@deprecated("use A2 instead") +class A: ... + +@deprecated("use B2 instead") +class B: ... + +@deprecated("use C2 instead") +class C: ... + +[file p/s.py] +from typing_extensions import deprecated + +@deprecated("use D2 instead") +class D: ... + +[file k.py] +from typing_extensions import deprecated + +@deprecated("use E2 instead") +class E: ... + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedClassInitMethod] +# flags: --enable-error-code=deprecated + +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: + def __init__(self) -> None: ... + +c: C # E: class __main__.C is deprecated: use C2 instead +C() # E: class __main__.C is deprecated: use C2 instead +C.__init__(c) # E: class __main__.C is deprecated: use C2 instead + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedSpecialMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator +from typing_extensions import deprecated + +class A: + @deprecated("no A + int") + def __add__(self, v: int) -> None: ... + + @deprecated("no int + A") + def __radd__(self, v: int) -> None: ... + + @deprecated("no A = A + int") + def __iadd__(self, v: int) -> A: ... + + @deprecated("no iteration") + def __iter__(self) -> Iterator[int]: ... + + @deprecated("no in") + def __contains__(self, v: int) -> int: ... + + @deprecated("no integer") + def __int__(self) -> int: ... + + @deprecated("no inversion") + def __invert__(self) -> A: ... + +class B: + @deprecated("still no in") + def __contains__(self, v: int) -> int: ... + +a = A() +b = B() +a + 1 # E: function __main__.A.__add__ is deprecated: no A + int +1 + a # E: function __main__.A.__radd__ is deprecated: no int + A +a += 1 # E: function __main__.A.__iadd__ is deprecated: no A = A + int +for i in a: # E: function __main__.A.__iter__ is deprecated: no iteration + reveal_type(i) # N: Revealed type is "builtins.int" +1 in a # E: function __main__.A.__contains__ is deprecated: no in +1 in b # E: function __main__.B.__contains__ is deprecated: still no in +~a # E: function __main__.A.__invert__ is deprecated: no inversion + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedOverloadedSpecialMethods] +# flags: --enable-error-code=deprecated + +from typing import Iterator, Union +from typing_extensions import deprecated, overload + +class A: + @overload + @deprecated("no A + int") + def __add__(self, v: int) -> None: ... + @overload + def __add__(self, v: str) -> None: ... + def __add__(self, v: Union[int, str]) -> None: ... + + @overload + def __radd__(self, v: int) -> None: ... + @overload + @deprecated("no str + A") + def __radd__(self, v: str) -> None: ... + def __radd__(self, v: Union[int, str]) -> None: ... + + @overload + def __iadd__(self, v: int) -> A: ... + @overload + def __iadd__(self, v: str) -> A: ... + @deprecated("no A += Any") + def __iadd__(self, v: Union[int, str]) -> A: ... + +a = A() +a + 1 # E: overload def (__main__.A, builtins.int) of function __main__.A.__add__ is deprecated: no A + int +a + "x" +1 + a +"x" + a # E: overload def (__main__.A, builtins.str) of function __main__.A.__radd__ is deprecated: no str + A +a += 1 # E: function __main__.A.__iadd__ is deprecated: no A += Any +a += "x" # E: function __main__.A.__iadd__ is deprecated: no A += Any + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedMethod] +# flags: --enable-error-code=deprecated + +from typing_extensions import deprecated + +class C: + @deprecated("use g instead") + def f(self) -> None: ... + + def g(self) -> None: ... + + @staticmethod + @deprecated("use g instead") + def h() -> None: ... + + @deprecated("use g instead") + @staticmethod + def k() -> None: ... + +C.f # E: function __main__.C.f is deprecated: use g instead +C().f # E: function __main__.C.f is deprecated: use g instead +C().f() # E: function __main__.C.f is deprecated: use g instead +C().f(1) # E: function __main__.C.f is deprecated: use g instead \ + # E: Too many arguments for "f" of "C" +f = C().f # E: function __main__.C.f is deprecated: use g instead +f() +t = (C.f, C.f, C.g) # E: function __main__.C.f is deprecated: use g instead + +C().g() +C().h() # E: function __main__.C.h is deprecated: use g instead +C().k() # E: function __main__.C.k is deprecated: use g instead + +[builtins fixtures/callable.pyi] + + +[case testDeprecatedClassWithDeprecatedMethod] +# flags: --enable-error-code=deprecated + +from typing_extensions import deprecated + +@deprecated("use D instead") +class C: + @deprecated("use g instead") + def f(self) -> None: ... + def g(self) -> None: ... + +C().f() # E: class __main__.C is deprecated: use D instead \ + # E: function __main__.C.f is deprecated: use g instead +C().g() # E: class __main__.C is deprecated: use D instead + +[builtins fixtures/callable.pyi] + + +[case testDeprecatedProperty] +# flags: --enable-error-code=deprecated + +from typing_extensions import deprecated + +class C: + @property + @deprecated("use f2 instead") + def f(self) -> int: ... + + @property + def g(self) -> int: ... + @g.setter + @deprecated("use g2 instead") + def g(self, v: int) -> None: ... + + +C.f # E: function __main__.C.f is deprecated: use f2 instead +C().f # E: function __main__.C.f is deprecated: use f2 instead +C().f() # E: function __main__.C.f is deprecated: use f2 instead \ + # E: "int" not callable +C().f = 1 # E: function __main__.C.f is deprecated: use f2 instead \ + # E: Property "f" defined in "C" is read-only + + +C.g +C().g +C().g = 1 # E: function __main__.C.g is deprecated: use g2 instead +C().g = "x" # E: function __main__.C.g is deprecated: use g2 instead \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + +[builtins fixtures/property.pyi] + + +[case testDeprecatedDescriptor] +# flags: --enable-error-code=deprecated + +from typing import Any, Optional, Union +from typing_extensions import deprecated, overload + +@deprecated("use E1 instead") +class D1: + def __get__(self, obj: Optional[C], objtype: Any) -> Union[D1, int]: ... + +class D2: + @deprecated("use E2.__get__ instead") + def __get__(self, obj: Optional[C], objtype: Any) -> Union[D2, int]: ... + + @deprecated("use E2.__set__ instead") + def __set__(self, obj: C, value: int) -> None: ... + +class D3: + @overload + @deprecated("use E3.__get__ instead") + def __get__(self, obj: None, objtype: Any) -> D3: ... + @overload + @deprecated("use E3.__get__ instead") + def __get__(self, obj: C, objtype: Any) -> int: ... + def __get__(self, obj: Optional[C], objtype: Any) -> Union[D3, int]: ... + + @overload + def __set__(self, obj: C, value: int) -> None: ... + @overload + @deprecated("use E3.__set__ instead") + def __set__(self, obj: C, value: str) -> None: ... + def __set__(self, obj: C, value: Union[int, str]) -> None: ... + +class C: + d1 = D1() # E: class __main__.D1 is deprecated: use E1 instead + d2 = D2() + d3 = D3() + +c: C +C.d1 +c.d1 +c.d1 = 1 + +C.d2 # E: function __main__.D2.__get__ is deprecated: use E2.__get__ instead +c.d2 # E: function __main__.D2.__get__ is deprecated: use E2.__get__ instead +c.d2 = 1 # E: function __main__.D2.__set__ is deprecated: use E2.__set__ instead + +C.d3 # E: overload def (self: __main__.D3, obj: None, objtype: Any) -> __main__.D3 of function __main__.D3.__get__ is deprecated: use E3.__get__ instead +c.d3 # E: overload def (self: __main__.D3, obj: __main__.C, objtype: Any) -> builtins.int of function __main__.D3.__get__ is deprecated: use E3.__get__ instead +c.d3 = 1 +c.d3 = "x" # E: overload def (self: __main__.D3, obj: __main__.C, value: builtins.str) of function __main__.D3.__set__ is deprecated: use E3.__set__ instead +[builtins fixtures/property.pyi] + + +[case testDeprecatedOverloadedFunction] +# flags: --enable-error-code=deprecated + +from typing import Union +from typing_extensions import deprecated, overload + +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +@deprecated("use f2 instead") +def f(x: Union[int, str]) -> Union[int, str]: ... + +f # E: function __main__.f is deprecated: use f2 instead +f(1) # E: function __main__.f is deprecated: use f2 instead +f("x") # E: function __main__.f is deprecated: use f2 instead +f(1.0) # E: function __main__.f is deprecated: use f2 instead \ + # E: No overload variant of "f" matches argument type "float" \ + # N: Possible overload variants: \ + # N: def f(x: int) -> int \ + # N: def f(x: str) -> str + +@overload +@deprecated("work with str instead") +def g(x: int) -> int: ... +@overload +def g(x: str) -> str: ... +def g(x: Union[int, str]) -> Union[int, str]: ... + +g +g(1) # E: overload def (x: builtins.int) -> builtins.int of function __main__.g is deprecated: work with str instead +g("x") +g(1.0) # E: No overload variant of "g" matches argument type "float" \ + # N: Possible overload variants: \ + # N: def g(x: int) -> int \ + # N: def g(x: str) -> str + +@overload +def h(x: int) -> int: ... +@deprecated("work with int instead") +@overload # N: @overload should be placed before @deprecated +def h(x: str) -> str: ... +def h(x: Union[int, str]) -> Union[int, str]: ... + +h +h(1) +h("x") # E: overload def (x: builtins.str) -> builtins.str of function __main__.h is deprecated: work with int instead +h(1.0) # E: No overload variant of "h" matches argument type "float" \ + # N: Possible overload variants: \ + # N: def h(x: int) -> int \ + # N: def h(x: str) -> str + +[builtins fixtures/tuple.pyi] + + +[case testDeprecatedImportedOverloadedFunction] +# flags: --enable-error-code=deprecated + +import m + +m.g +m.g(1) # E: overload def (x: builtins.int) -> builtins.int of function m.g is deprecated: work with str instead +m.g("x") + +[file m.py] + +from typing import Union +from typing_extensions import deprecated, overload + +@overload +@deprecated("work with str instead") +def g(x: int) -> int: ... +@overload +def g(x: str) -> str: ... +def g(x: Union[int, str]) -> Union[int, str]: ... + +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 78a114eda7642..593cf43dc3a9b 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -11,6 +11,8 @@ m = Medal.gold if int(): m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") +[builtins fixtures/enum.pyi] + -- Creation from Enum call -- ----------------------- @@ -79,6 +81,7 @@ from typing import Generic, TypeVar T = TypeVar("T") class Medal(Generic[T], metaclass=EnumMeta): # E: Enum class cannot be generic q = None +[builtins fixtures/enum.pyi] [case testEnumNameAndValue] from enum import Enum @@ -175,6 +178,104 @@ def infer_truth(truth: Truth) -> None: reveal_type(truth.value) # N: Revealed type is "builtins.bool" [builtins fixtures/bool.pyi] +[case testEnumTruthyness] +# mypy: warn-unreachable +import enum +from typing_extensions import Literal + +class E(enum.Enum): + zero = 0 + one = 1 + +def print(s: str) -> None: ... + +if E.zero: + print("zero is true") +if not E.zero: + print("zero is false") # E: Statement is unreachable + +if E.one: + print("one is true") +if not E.one: + print("one is false") # E: Statement is unreachable + +def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: + if zero: + print("zero is true") + if not zero: + print("zero is false") # E: Statement is unreachable + if one: + print("one is true") + if not one: + print("one is false") # E: Statement is unreachable +[builtins fixtures/tuple.pyi] + +[case testEnumTruthynessCustomDunderBool] +# mypy: warn-unreachable +import enum +from typing_extensions import Literal + +class E(enum.Enum): + zero = 0 + one = 1 + def __bool__(self) -> Literal[False]: + return False + +def print(s: str) -> None: ... + +if E.zero: + print("zero is true") # E: Statement is unreachable +if not E.zero: + print("zero is false") + +if E.one: + print("one is true") # E: Statement is unreachable +if not E.one: + print("one is false") + +def main(zero: Literal[E.zero], one: Literal[E.one]) -> None: + if zero: + print("zero is true") # E: Statement is unreachable + if not zero: + print("zero is false") + if one: + print("one is true") # E: Statement is unreachable + if not one: + print("one is false") +[builtins fixtures/enum.pyi] + +[case testEnumTruthynessStrEnum] +# mypy: warn-unreachable +import enum +from typing_extensions import Literal + +class E(enum.StrEnum): + empty = "" + not_empty = "asdf" + +def print(s: str) -> None: ... + +if E.empty: + print("empty is true") +if not E.empty: + print("empty is false") + +if E.not_empty: + print("not_empty is true") +if not E.not_empty: + print("not_empty is false") + +def main(empty: Literal[E.empty], not_empty: Literal[E.not_empty]) -> None: + if empty: + print("empty is true") + if not empty: + print("empty is false") + if not_empty: + print("not_empty is true") + if not not_empty: + print("not_empty is false") +[builtins fixtures/enum.pyi] + [case testEnumUnique] import enum @enum.unique @@ -183,6 +284,7 @@ class E(enum.Enum): y = 1 # NOTE: This duplicate value is not detected by mypy at the moment x = 1 x = E.x +[builtins fixtures/enum.pyi] [out] main:7: error: Incompatible types in assignment (expression has type "E", variable has type "int") @@ -197,6 +299,7 @@ if int(): s = '' if int(): s = N.y # E: Incompatible types in assignment (expression has type "N", variable has type "str") +[builtins fixtures/enum.pyi] [case testIntEnum_functionTakingIntEnum] from enum import IntEnum @@ -207,6 +310,7 @@ def takes_some_int_enum(n: SomeIntEnum): takes_some_int_enum(SomeIntEnum.x) takes_some_int_enum(1) # Error takes_some_int_enum(SomeIntEnum(1)) # How to deal with the above +[builtins fixtures/enum.pyi] [out] main:7: error: Argument 1 to "takes_some_int_enum" has incompatible type "int"; expected "SomeIntEnum" @@ -218,6 +322,7 @@ def takes_int(i: int): pass takes_int(SomeIntEnum.x) takes_int(2) +[builtins fixtures/enum.pyi] [case testIntEnum_functionReturningIntEnum] from enum import IntEnum @@ -230,6 +335,7 @@ an_int = returns_some_int_enum() an_enum = SomeIntEnum.x an_enum = returns_some_int_enum() +[builtins fixtures/enum.pyi] [out] [case testStrEnumCreation] @@ -244,6 +350,7 @@ reveal_type(MyStrEnum.x) # N: Revealed type is "Literal[__main__.MyStrEnum.x]?" reveal_type(MyStrEnum.x.value) # N: Revealed type is "Literal['x']?" reveal_type(MyStrEnum.y) # N: Revealed type is "Literal[__main__.MyStrEnum.y]?" reveal_type(MyStrEnum.y.value) # N: Revealed type is "Literal['y']?" +[builtins fixtures/enum.pyi] [out] [case testEnumMethods] @@ -278,6 +385,7 @@ takes_int(SomeExtIntEnum.x) def takes_some_ext_int_enum(s: SomeExtIntEnum): pass takes_some_ext_int_enum(SomeExtIntEnum.x) +[builtins fixtures/enum.pyi] [case testNamedTupleEnum] from typing import NamedTuple @@ -299,6 +407,7 @@ class E(IntEnum): a = 1 x: int reveal_type(E(x)) +[builtins fixtures/tuple.pyi] [out] main:5: note: Revealed type is "__main__.E" @@ -308,6 +417,7 @@ class E(IntEnum): a = 1 s: str reveal_type(E[s]) +[builtins fixtures/enum.pyi] [out] main:5: note: Revealed type is "__main__.E" @@ -317,6 +427,7 @@ class E(IntEnum): a = 1 E[1] # E: Enum index should be a string (actual index type "int") x = E[1] # E: Enum index should be a string (actual index type "int") +[builtins fixtures/enum.pyi] [case testEnumIndexIsNotAnAlias] from enum import Enum @@ -334,6 +445,7 @@ def get_member(name: str) -> E: return val reveal_type(get_member('a')) # N: Revealed type is "__main__.E" +[builtins fixtures/enum.pyi] [case testGenericEnum] from enum import Enum @@ -346,6 +458,7 @@ class F(Generic[T], Enum): # E: Enum class cannot be generic y: T reveal_type(F[int].x) # N: Revealed type is "__main__.F[builtins.int]" +[builtins fixtures/enum.pyi] [case testEnumFlag] from enum import Flag @@ -357,6 +470,7 @@ if int(): x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "C") if int(): x = x | C.b +[builtins fixtures/enum.pyi] [case testEnumIntFlag] from enum import IntFlag @@ -368,6 +482,7 @@ if int(): x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "C") if int(): x = x | C.b +[builtins fixtures/enum.pyi] [case testAnonymousEnum] from enum import Enum @@ -378,6 +493,7 @@ class A: self.x = E.a a = A() reveal_type(a.x) +[builtins fixtures/enum.pyi] [out] main:8: note: Revealed type is "__main__.E@4" @@ -393,6 +509,7 @@ x = A.E.a y = B.E.a if int(): x = y # E: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") +[builtins fixtures/enum.pyi] [case testFunctionalEnumString] from enum import Enum, IntEnum @@ -402,6 +519,7 @@ reveal_type(E.foo) reveal_type(E.bar.value) reveal_type(I.bar) reveal_type(I.baz.value) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Any" @@ -414,6 +532,7 @@ E = Enum('E', ('foo', 'bar')) F = IntEnum('F', ['bar', 'baz']) reveal_type(E.foo) reveal_type(F.baz) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Literal[__main__.F.baz]?" @@ -426,6 +545,7 @@ reveal_type(E.foo) reveal_type(F.baz) reveal_type(E.foo.value) reveal_type(F.bar.name) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Literal[__main__.F.baz]?" @@ -440,6 +560,7 @@ reveal_type(E.foo) reveal_type(F.baz) reveal_type(E.foo.value) reveal_type(F.bar.name) +[builtins fixtures/enum.pyi] [out] main:4: note: Revealed type is "Literal[__main__.E.foo]?" main:5: note: Revealed type is "Literal[__main__.F.baz]?" @@ -455,6 +576,7 @@ fake_enum1 = Enum('fake_enum1', ['a', 'b']) fake_enum2 = Enum('fake_enum2', names=['a', 'b']) fake_enum3 = Enum(value='fake_enum3', names=['a', 'b']) fake_enum4 = Enum(value='fake_enum4', names=['a', 'b'] , module=__name__) +[builtins fixtures/enum.pyi] [case testFunctionalEnumErrors] from enum import Enum, IntEnum @@ -484,6 +606,7 @@ X = Enum('Something', 'a b') # E: String argument 1 "Something" to enum.Enum(.. reveal_type(X.a) # N: Revealed type is "Literal[__main__.Something@23.a]?" X.asdf # E: "Type[Something@23]" has no attribute "asdf" +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [case testFunctionalEnumFlag] @@ -498,6 +621,7 @@ reveal_type(B.a.name) # N: Revealed type is "Literal['a']?" # TODO: The revealed type should be 'int' here reveal_type(A.x.value) # N: Revealed type is "Any" reveal_type(B.a.value) # N: Revealed type is "Any" +[builtins fixtures/enum.pyi] [case testAnonymousFunctionalEnum] from enum import Enum @@ -507,6 +631,7 @@ class A: self.x = E.a a = A() reveal_type(a.x) +[builtins fixtures/enum.pyi] [out] main:7: note: Revealed type is "__main__.A.E@4" @@ -520,6 +645,7 @@ x = A.E.a y = B.E.a if int(): x = y # E: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") +[builtins fixtures/enum.pyi] [case testFunctionalEnumProtocols] from enum import IntEnum @@ -537,6 +663,7 @@ a: E = E.x # type: ignore[used-before-def] class E(Enum): x = 1 y = 2 +[builtins fixtures/enum.pyi] [out] [case testEnumWorkWithForward2] @@ -547,6 +674,7 @@ F = Enum('F', {'x': 1, 'y': 2}) def fn(x: F) -> None: pass fn(b) +[builtins fixtures/enum.pyi] [out] [case testFunctionalEnum] @@ -564,6 +692,7 @@ Gu.a Gb.a Hu.a Hb.a +[builtins fixtures/enum.pyi] [out] [case testEnumIncremental] @@ -576,6 +705,7 @@ class E(Enum): a = 1 b = 2 F = Enum('F', 'a b') +[builtins fixtures/enum.pyi] [rechecked] [stale] [out1] @@ -734,6 +864,7 @@ class SomeEnum(Enum): from enum import Enum class SomeEnum(Enum): a = "foo" +[builtins fixtures/enum.pyi] [out] main:2: note: Revealed type is "Literal[1]?" [out2] @@ -757,7 +888,7 @@ elif x is Foo.C: reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.C]" else: reveal_type(x) # No output here: this branch is unreachable -reveal_type(x) # N: Revealed type is "__main__.Foo" +reveal_type(x) # N: Revealed type is "Union[Literal[__main__.Foo.A], Literal[__main__.Foo.B], Literal[__main__.Foo.C]]" if Foo.A is x: reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.A]" @@ -767,7 +898,7 @@ elif Foo.C is x: reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.C]" else: reveal_type(x) # No output here: this branch is unreachable -reveal_type(x) # N: Revealed type is "__main__.Foo" +reveal_type(x) # N: Revealed type is "Union[Literal[__main__.Foo.A], Literal[__main__.Foo.B], Literal[__main__.Foo.C]]" y: Foo if y is Foo.A: @@ -949,7 +1080,7 @@ x: Optional[Foo] if x: reveal_type(x) # N: Revealed type is "__main__.Foo" else: - reveal_type(x) # N: Revealed type is "Union[__main__.Foo, None]" + reveal_type(x) # N: Revealed type is "None" if x is not None: reveal_type(x) # N: Revealed type is "__main__.Foo" @@ -961,7 +1092,7 @@ if x is Foo.A: else: reveal_type(x) # N: Revealed type is "Union[Literal[__main__.Foo.B], Literal[__main__.Foo.C], None]" reveal_type(x) # N: Revealed type is "Union[__main__.Foo, None]" -[builtins fixtures/bool.pyi] +[builtins fixtures/enum.pyi] [case testEnumReachabilityWithMultipleEnums] from enum import Enum @@ -1104,6 +1235,7 @@ class A: self.b = Enum("b", [("foo", "bar")]) # E: Enum type as attribute is not supported reveal_type(A().b) # N: Revealed type is "Any" +[builtins fixtures/enum.pyi] [case testEnumReachabilityWithChaining] from enum import Enum @@ -1307,6 +1439,7 @@ class Foo(Enum): a = Foo.A reveal_type(a.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" reveal_type(a._value_) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" +[builtins fixtures/enum.pyi] [case testNewSetsUnexpectedValueType] from enum import Enum @@ -1325,7 +1458,7 @@ class Foo(bytes, Enum): a = Foo.A reveal_type(a.value) # N: Revealed type is "Any" reveal_type(a._value_) # N: Revealed type is "Any" -[builtins fixtures/primitives.pyi] +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [case testValueTypeWithNewInParentClass] @@ -1398,6 +1531,7 @@ class E(IntEnum): A = N(0) reveal_type(E.A.value) # N: Revealed type is "__main__.N" +[builtins fixtures/enum.pyi] [case testEnumFinalValues] @@ -1410,6 +1544,7 @@ class Medal(Enum): Medal.gold = 0 # E: Cannot assign to final attribute "gold" # Same value: Medal.silver = 2 # E: Cannot assign to final attribute "silver" +[builtins fixtures/enum.pyi] [case testEnumFinalValuesCannotRedefineValueProp] @@ -1417,6 +1552,7 @@ from enum import Enum class Types(Enum): key = 0 value = 1 +[builtins fixtures/enum.pyi] [case testEnumReusedKeys] @@ -1764,7 +1900,8 @@ class B(A): x = 1 # E: Cannot override writable attribute "x" with a final one class A1(Enum): - x: int = 1 + x: int = 1 # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members class B1(A1): # E: Cannot extend enum with existing members: "A1" pass @@ -1779,6 +1916,7 @@ class A3(Enum): x: Final[int] # type: ignore class B3(A3): x = 1 # E: Cannot override final attribute "x" (previously declared in base class "A3") + [builtins fixtures/bool.pyi] [case testEnumNotFinalWithMethodsAndUninitializedValuesStub] @@ -1786,14 +1924,17 @@ import lib [file lib.pyi] from enum import Enum -class A(Enum): +class A(Enum): # E: Detected enum "lib.A" in a type stub with zero members. There is a chance this is due to a recent change in the semantics of enum membership. If so, use `member = value` to mark an enum member, instead of `member: type` \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members x: int class B(A): # E: Cannot extend enum with existing members: "A" x = 1 # E: Cannot override writable attribute "x" with a final one class C(Enum): x = 1 -class D(C): # E: Cannot extend enum with existing members: "C" +class D(C): # E: Cannot extend enum with existing members: "C" \ + # E: Detected enum "lib.D" in a type stub with zero members. There is a chance this is due to a recent change in the semantics of enum membership. If so, use `member = value` to mark an enum member, instead of `member: type` \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members x: int # E: Cannot assign to final name "x" [builtins fixtures/bool.pyi] @@ -2030,7 +2171,7 @@ class C(Enum): _ignore_ = 'X' C._ignore_ # E: "Type[C]" has no attribute "_ignore_" -[typing fixtures/typing-medium.pyi] +[builtins fixtures/enum.pyi] [case testCanOverrideDunderAttributes] import typing @@ -2109,6 +2250,7 @@ class AllPartialList(Enum): def check(self) -> None: reveal_type(self.value) # N: Revealed type is "builtins.list[Any]" +[builtins fixtures/tuple.pyi] [case testEnumPrivateAttributeNotMember] from enum import Enum @@ -2120,6 +2262,7 @@ class MyEnum(Enum): # TODO: change the next line to use MyEnum._MyEnum__my_dict when mypy implements name mangling x: MyEnum = MyEnum.__my_dict # E: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "MyEnum") +[builtins fixtures/enum.pyi] [case testEnumWithPrivateAttributeReachability] # flags: --warn-unreachable @@ -2185,3 +2328,69 @@ reveal_type(A.y.value) # N: Revealed type is "Literal[2]?" def some_a(a: A): reveal_type(a.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" [builtins fixtures/dict.pyi] + + +[case testErrorOnAnnotatedMember] +from enum import Enum + +class Medal(Enum): + gold: int = 1 # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members + silver: str = 2 # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + bronze = 3 +[builtins fixtures/enum.pyi] + +[case testEnumMemberWithPlaceholder] +from enum import Enum + +class Pet(Enum): + CAT = ... + DOG: str = ... # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "str") +[builtins fixtures/enum.pyi] + +[case testEnumValueWithPlaceholderNodeType] +# https://github.com/python/mypy/issues/11971 +from enum import Enum +from typing import Any, Callable, Dict +class Foo(Enum): + Bar: Foo = Callable[[str], None] # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Incompatible types in assignment (expression has type "", variable has type "Foo") + Baz: Any = Callable[[Dict[str, "Missing"]], None] # E: Enum members must be left unannotated \ + # N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \ + # E: Type application targets a non-generic function or class \ + # E: Name "Missing" is not defined + +reveal_type(Foo.Bar) # N: Revealed type is "Literal[__main__.Foo.Bar]?" +reveal_type(Foo.Bar.value) # N: Revealed type is "__main__.Foo" +reveal_type(Foo.Baz) # N: Revealed type is "Literal[__main__.Foo.Baz]?" +reveal_type(Foo.Baz.value) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + + +[case testEnumWithOnlyImplicitMembersUsingAnnotationOnly] +# flags: --warn-unreachable +import enum + + +class E(enum.IntEnum): + A: int + B: int + + +def do_check(value: E) -> None: + reveal_type(value) # N: Revealed type is "__main__.E" + # this is a nonmember check, not an emum member check, and it should not narrow the value + if value is E.A: + return + + reveal_type(value) # N: Revealed type is "__main__.E" + "should be reachable" + +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 961815b118174..10cc145d0c70b 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -460,7 +460,7 @@ a: D = {'x': ''} # E: Incompatible types (expression has type "str", TypedDict b: D = {'y': ''} # E: Missing key "x" for TypedDict "D" [typeddict-item] \ # E: Extra key "y" for TypedDict "D" [typeddict-unknown-key] c = D(x=0) if int() else E(x=0, y=0) -c = {} # E: Expected TypedDict key "x" but found no keys [typeddict-item] +c = {} # E: Missing key "x" for TypedDict "D" [typeddict-item] d: D = {'x': '', 'y': 1} # E: Extra key "y" for TypedDict "D" [typeddict-unknown-key] \ # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") [typeddict-item] @@ -846,34 +846,48 @@ foo = Foo() if foo: # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass +not foo # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] + zero = 0 if zero: pass +not zero + false = False if false: pass +not false + null = None if null: pass +not null + s = '' if s: pass +not s + good_union: Union[str, int] = 5 if good_union: pass if not good_union: pass +not good_union + bad_union: Union[Foo, Bar] = Foo() if bad_union: # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass if not bad_union: # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass +not bad_union # E: "__main__.bad_union" has type "Union[Foo, Bar]" of which no members implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] + # 'object' is special and is treated as potentially falsy obj: object = Foo() if obj: @@ -881,18 +895,26 @@ if obj: if not obj: pass +not obj + lst: List[int] = [] if lst: pass +not lst + a: Any if a: pass +not a + any_or_object: Union[object, Any] if any_or_object: pass +not any_or_object + if (my_foo := Foo()): # E: "__main__.my_foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool] pass @@ -909,6 +931,8 @@ if not f: # E: Function "f" could always be true in boolean context [truthy-fu pass conditional_result = 'foo' if f else 'bar' # E: Function "f" could always be true in boolean context [truthy-function] +not f # E: Function "f" could always be true in boolean context [truthy-function] + [case testTruthyIterable] # flags: --enable-error-code truthy-iterable from typing import Iterable @@ -916,6 +940,8 @@ def func(var: Iterable[str]) -> None: if var: # E: "var" has type "Iterable[str]" which can always be true in boolean context. Consider using "Collection[str]" instead. [truthy-iterable] ... + not var # E: "var" has type "Iterable[str]" which can always be true in boolean context. Consider using "Collection[str]" instead. [truthy-iterable] + [case testNoOverloadImplementation] from typing import overload @@ -1196,3 +1222,17 @@ def f(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of inpu pass [builtins fixtures/tuple.pyi] + + +[case testOverloadedFunctionSignature] +from typing import overload, Union + +@overload +def process(response1: float,response2: float) -> float: + ... +@overload +def process(response1: int,response2: int) -> int: # E: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] + ... + +def process(response1,response2)-> Union[float,int]: + return response1 + response2 diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index f9bd60f4dcc8f..cd26c9bb408af 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -342,7 +342,7 @@ b: bool i: str j = b or i if not j: - reveal_type(j) # N: Revealed type is "builtins.str" + reveal_type(j) # N: Revealed type is "Literal['']" [builtins fixtures/bool.pyi] [case testAndOr] @@ -1178,8 +1178,8 @@ class B: pass [case testSlicingWithInvalidBase] a: A -a[1:2] # E: Invalid index type "slice" for "A"; expected type "int" -a[:] # E: Invalid index type "slice" for "A"; expected type "int" +a[1:2] # E: Invalid index type "slice[int, int, None]" for "A"; expected type "int" +a[:] # E: Invalid index type "slice[None, None, None]" for "A"; expected type "int" class A: def __getitem__(self, n: int) -> 'A': pass [builtins fixtures/slice.pyi] @@ -1470,10 +1470,9 @@ if int(): [case testConditionalExpressionUnion] from typing import Union -reveal_type(1 if bool() else 2) # N: Revealed type is "builtins.int" -reveal_type(1 if bool() else '') # N: Revealed type is "builtins.object" -x: Union[int, str] = reveal_type(1 if bool() else '') \ - # N: Revealed type is "Union[Literal[1]?, Literal['']?]" +reveal_type(1 if bool() else 2) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]" +reveal_type(1 if bool() else '') # N: Revealed type is "Union[Literal[1]?, Literal['']?]" +x: Union[int, str] = reveal_type(1 if bool() else '') # N: Revealed type is "Union[Literal[1]?, Literal['']?]" class A: pass class B(A): @@ -1487,17 +1486,17 @@ b = B() c = C() d = D() reveal_type(a if bool() else b) # N: Revealed type is "__main__.A" -reveal_type(b if bool() else c) # N: Revealed type is "builtins.object" -reveal_type(c if bool() else b) # N: Revealed type is "builtins.object" -reveal_type(c if bool() else a) # N: Revealed type is "builtins.object" -reveal_type(d if bool() else b) # N: Revealed type is "__main__.A" +reveal_type(b if bool() else c) # N: Revealed type is "Union[__main__.B, __main__.C]" +reveal_type(c if bool() else b) # N: Revealed type is "Union[__main__.C, __main__.B]" +reveal_type(c if bool() else a) # N: Revealed type is "Union[__main__.C, __main__.A]" +reveal_type(d if bool() else b) # N: Revealed type is "Union[__main__.D, __main__.B]" [builtins fixtures/bool.pyi] [case testConditionalExpressionUnionWithAny] from typing import Union, Any a: Any x: Union[int, str] = reveal_type(a if int() else 1) # N: Revealed type is "Union[Any, Literal[1]?]" -reveal_type(a if int() else 1) # N: Revealed type is "Any" +reveal_type(a if int() else 1) # N: Revealed type is "Union[Any, Literal[1]?]" [case testConditionalExpressionStatementNoReturn] from typing import List, Union diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index dadf76a283b01..763183159e94f 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -6,13 +6,11 @@ [case testFinalDefiningModuleVar] from typing import Final -w: 'Final' = int() x: Final = int() y: Final[float] = int() z: Final[int] = int() bad: Final[str] = int() # E: Incompatible types in assignment (expression has type "int", variable has type "str") -reveal_type(w) # N: Revealed type is "builtins.int" reveal_type(x) # N: Revealed type is "builtins.int" reveal_type(y) # N: Revealed type is "builtins.float" reveal_type(z) # N: Revealed type is "builtins.int" diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 62711d5f0071a..c6419923ebc62 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -79,6 +79,13 @@ async def g(x: int) -> Any: [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] +[case testDisallowUntypedDefsAndGeneric] +# flags: --disallow-untyped-defs --disallow-any-generics +def get_tasks(self): + return 'whatever' +[out] +main:2: error: Function is missing a return type annotation + [case testDisallowUntypedDefsUntypedDecorator] # flags: --disallow-untyped-decorators def d(p): @@ -540,21 +547,30 @@ tmp/b.py:1: error: Unsupported operand types for + ("int" and "str") [case testFollowImportsNormal] # flags: --follow-imports=normal from mod import x -x + "" +x + 0 +x + "" # E: Unsupported operand types for + ("int" and "str") +import mod +mod.x + 0 +mod.x + "" # E: Unsupported operand types for + ("int" and "str") +mod.y # E: "object" has no attribute "y" +mod + 0 # E: Unsupported left operand type for + ("object") [file mod.py] -1 + "" +1 + "" # E: Unsupported operand types for + ("int" and "str") x = 0 -[out] -tmp/mod.py:1: error: Unsupported operand types for + ("int" and "str") -main:3: error: Unsupported operand types for + ("int" and "str") +x += "" # E: Unsupported operand types for + ("int" and "str") [case testFollowImportsSilent] # flags: --follow-imports=silent from mod import x x + "" # E: Unsupported operand types for + ("int" and "str") +import mod +mod.x + "" # E: Unsupported operand types for + ("int" and "str") +mod.y # E: "object" has no attribute "y" +mod + 0 # E: Unsupported left operand type for + ("object") [file mod.py] 1 + "" x = 0 +x += "" [case testFollowImportsSilentTypeIgnore] # flags: --warn-unused-ignores --follow-imports=silent @@ -565,20 +581,55 @@ x = 3 # type: ignore [case testFollowImportsSkip] # flags: --follow-imports=skip from mod import x +reveal_type(x) # N: Revealed type is "Any" x + "" +import mod +reveal_type(mod.x) # N: Revealed type is "Any" [file mod.py] this deliberate syntax error will not be reported -[out] [case testFollowImportsError] # flags: --follow-imports=error -from mod import x +from mod import x # E: Import of "mod" ignored \ + # N: (Using --follow-imports=error, module not passed on command line) x + "" +reveal_type(x) # N: Revealed type is "Any" +import mod +reveal_type(mod.x) # N: Revealed type is "Any" [file mod.py] deliberate syntax error -[out] -main:2: error: Import of "mod" ignored -main:2: note: (Using --follow-imports=error, module not passed on command line) + +[case testFollowImportsSelective] +# flags: --config-file tmp/mypy.ini +import normal +import silent +import skip +import error # E: Import of "error" ignored \ + # N: (Using --follow-imports=error, module not passed on command line) +reveal_type(normal.x) # N: Revealed type is "builtins.int" +reveal_type(silent.x) # N: Revealed type is "builtins.int" +reveal_type(skip) # N: Revealed type is "Any" +reveal_type(error) # N: Revealed type is "Any" +[file mypy.ini] +\[mypy] +\[mypy-normal] +follow_imports = normal +\[mypy-silent] +follow_imports = silent +\[mypy-skip] +follow_imports = skip +\[mypy-error] +follow_imports = error +[file normal.py] +x = 0 +x += '' # E: Unsupported operand types for + ("int" and "str") +[file silent.py] +x = 0 +x += '' +[file skip.py] +bla bla +[file error.py] +bla bla [case testIgnoreMissingImportsFalse] from mod import x @@ -591,6 +642,15 @@ main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missin from mod import x [out] +[case testNoConfigFile] +# flags: --config-file= +# type: ignore + +[file mypy.ini] +\[mypy] +warn_unused_ignores = True +[out] + [case testPerFileIncompleteDefsBasic] # flags: --config-file tmp/mypy.ini import standard, incomplete @@ -868,6 +928,16 @@ implicit_optional = true module = 'optional' strict_optional = true +[case testSilentMissingImportsOff] +-- ignore_missing_imports is False by default. +import missing # E: Cannot find implementation or library stub for module named "missing" \ + # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +reveal_type(missing.x) # N: Revealed type is "Any" + +[case testSilentMissingImportsOn] +# flags: --ignore-missing-imports +import missing +reveal_type(missing.x) # N: Revealed type is "Any" [case testDisallowImplicitTypesIgnoreMissingTypes] # flags: --ignore-missing-imports --disallow-any-unimported @@ -893,6 +963,12 @@ from missing import Unchecked t: Unchecked = 12 # E: Type of variable becomes "Any" due to an unfollowed import +[case testAllowImplicitAnyVariableDefinition] +# flags: --ignore-missing-imports --allow-any-unimported +from missing import Unchecked + +t: Unchecked = 12 + [case testDisallowImplicitAnyGeneric] # flags: --ignore-missing-imports --disallow-any-unimported from missing import Unchecked @@ -924,9 +1000,9 @@ class A(List[Unchecked]): # E: Base type becomes "List[Any]" due to an unfollowe from missing import Unchecked from typing import List -X = List[Unchecked] +X = List[Unchecked] # E: Type alias target becomes "List[Any]" due to an unfollowed import -def f(x: X) -> None: # E: Argument 1 to "f" becomes "List[Any]" due to an unfollowed import +def f(x: X) -> None: pass [builtins fixtures/list.pyi] @@ -1447,6 +1523,29 @@ class Queue(Generic[_T]): ... [builtins fixtures/async_await.pyi] [typing fixtures/typing-full.pyi] +[case testDisallowAnyGenericsBuiltinTuplePre39] +# flags: --disallow-any-generics --python-version 3.8 +s = tuple([1, 2, 3]) +def f(t: tuple) -> None: pass # E: Implicit generic "Any". Use "typing.Tuple" and specify generic parameters +[builtins fixtures/tuple.pyi] + +[case testDisallowAnyGenericsBuiltinListPre39] +# flags: --disallow-any-generics --python-version 3.8 +l = list([1, 2, 3]) +def f(t: list) -> None: pass # E: Implicit generic "Any". Use "typing.List" and specify generic parameters +[builtins fixtures/list.pyi] + +[case testDisallowAnyGenericsBuiltinSetPre39] +# flags: --disallow-any-generics --python-version 3.8 +l = set({1, 2, 3}) +def f(s: set) -> None: pass # E: Implicit generic "Any". Use "typing.Set" and specify generic parameters +[builtins fixtures/set.pyi] + +[case testDisallowAnyGenericsBuiltinDictPre39] +# flags: --disallow-any-generics --python-version 3.8 +l = dict([('a', 1)]) +def f(d: dict) -> None: pass # E: Implicit generic "Any". Use "typing.Dict" and specify generic parameters +[builtins fixtures/dict.pyi] [case testCheckDefaultAllowAnyGeneric] from typing import TypeVar, Callable @@ -1863,8 +1962,9 @@ x: Tuple = () # E: Missing type parameters for generic type "Tuple" # flags: --disallow-any-generics from typing import Tuple, List -def f(s: List[Tuple]) -> None: pass # E: Missing type parameters for generic type "Tuple" -def g(s: List[Tuple[str, str]]) -> None: pass # no error +def f(s: Tuple) -> None: pass # E: Missing type parameters for generic type "Tuple" +def g(s: List[Tuple]) -> None: pass # E: Missing type parameters for generic type "Tuple" +def h(s: List[Tuple[str, str]]) -> None: pass # no error [builtins fixtures/list.pyi] [case testDisallowAnyGenericsTypeType] @@ -1908,14 +2008,36 @@ x: A = ('a', 'b', 1) # E: Missing type parameters for generic type "A" from typing import List def f(l: List) -> None: pass # E: Missing type parameters for generic type "List" -def g(l: List[str]) -> None: pass # no error +def g(l: List[str]) -> None: pass def h(l: List[List]) -> None: pass # E: Missing type parameters for generic type "List" def i(l: List[List[List[List]]]) -> None: pass # E: Missing type parameters for generic type "List" +def j() -> List: pass # E: Missing type parameters for generic type "List" x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") y: List = [] # E: Missing type parameters for generic type "List" [builtins fixtures/list.pyi] +[case testDisallowAnyGenericsPlainDict] +# flags: --disallow-any-generics +from typing import List, Dict + +def f(d: Dict) -> None: pass # E: Missing type parameters for generic type "Dict" +def g(d: Dict[str, Dict]) -> None: pass # E: Missing type parameters for generic type "Dict" +def h(d: List[Dict]) -> None: pass # E: Missing type parameters for generic type "Dict" + +d: Dict = {} # E: Missing type parameters for generic type "Dict" +[builtins fixtures/dict.pyi] + +[case testDisallowAnyGenericsPlainSet] +# flags: --disallow-any-generics +from typing import Set + +def f(s: Set) -> None: pass # E: Missing type parameters for generic type "Set" +def g(s: Set[Set]) -> None: pass # E: Missing type parameters for generic type "Set" + +s: Set = set() # E: Missing type parameters for generic type "Set" +[builtins fixtures/set.pyi] + [case testDisallowAnyGenericsCustomGenericClass] # flags: --disallow-any-generics from typing import Generic, TypeVar, Any @@ -2162,6 +2284,38 @@ allow_untyped_defs = True allow_untyped_calls = True disable_error_code = var-annotated +[case testPerFileIgnoreErrors] +# flags: --config-file tmp/mypy.ini +import foo, bar +[file foo.py] +x: str = 5 +[file bar.py] +x: str = 5 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +[file mypy.ini] +\[mypy] +\[mypy-foo] +ignore_errors = True + +[case testPerFileUntypedDefs] +# flags: --config-file tmp/mypy.ini +import x, y, z +[file x.py] +def f(a): ... # E: Function is missing a type annotation +def g(a: int) -> int: return f(a) +[file y.py] +def f(a): pass +def g(a: int) -> int: return f(a) +[file z.py] +def f(a): pass # E: Function is missing a type annotation +def g(a: int) -> int: return f(a) # E: Call to untyped function "f" in typed context +[file mypy.ini] +\[mypy] +disallow_untyped_defs = True +\[mypy-y] +disallow_untyped_defs = False +\[mypy-z] +disallow_untyped_calls = True + [case testPerModuleErrorCodesOverride] # flags: --config-file tmp/mypy.ini import tests.foo @@ -2284,3 +2438,8 @@ class C(Generic[T]): ... A = Union[C, List] # OK [builtins fixtures/list.pyi] + +[case testNotesOnlyResultInExitSuccess] +-- check_untyped_defs is False by default. +def f(): + x: int = "no" # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 93540e203c36c..b8a02a1ec7d42 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1428,6 +1428,20 @@ else: # N: Redefinition: \ # N: def f(x: int = ...) -> None +[case testIncompatibleConditionalFunctionDefinition4] +from typing import Any, Union, TypeVar +T1 = TypeVar('T1') +T2 = TypeVar('T2', bound=Union[int, str]) +x = None # type: Any +if x: + def f(x: T1) -> T1: pass +else: + def f(x: T2) -> T2: pass # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def [T1] f(x: T1) -> T1 \ + # N: Redefinition: \ + # N: def [T2: Union[int, str]] f(x: T2) -> T2 + [case testConditionalFunctionDefinitionUsingDecorator1] from typing import Callable @@ -1460,7 +1474,7 @@ def dec(f) -> Callable[[int], None]: pass x = int() if x: - def f(x: int) -> None: pass + def f(x: int, /) -> None: pass else: @dec def f(): pass @@ -1475,9 +1489,12 @@ x = int() if x: def f(x: str) -> None: pass else: - # TODO: Complain about incompatible redefinition @dec - def f(): pass + def f(): pass # E: All conditional function variants must have identical signatures \ + # N: Original: \ + # N: def f(x: str) -> None \ + # N: Redefinition: \ + # N: def f(int, /) -> None [case testConditionalFunctionDefinitionUnreachable] def bar() -> None: @@ -1585,7 +1602,7 @@ else: def f(): yield [file m.py] -def f(): pass +def f() -> None: pass [case testDefineConditionallyAsImportedAndDecoratedWithInference] if int(): @@ -1779,6 +1796,7 @@ def Arg(x, y): pass F = Callable[[Arg(int, 'x')], int] # E: Invalid argument constructor "__main__.Arg" [case testCallableParsingFromExpr] +# flags: --python-version 3.9 from typing import Callable, List from mypy_extensions import Arg, VarArg, KwArg import mypy_extensions @@ -1799,10 +1817,23 @@ L = Callable[[Arg(name='x', type=int)], int] # ok # I have commented out the following test because I don't know how to expect the "defined here" note part of the error. # M = Callable[[Arg(gnome='x', type=int)], int] E: Invalid type alias: expression is not a valid type E: Unexpected keyword argument "gnome" for "Arg" N = Callable[[Arg(name=None, type=int)], int] # ok -O = Callable[[List[Arg(int)]], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: Type expected within [...] +O = Callable[[List[Arg(int)]], int] # E: Invalid type alias: expression is not a valid type \ + # E: Value of type "int" is not indexable \ + # E: Type expected within [...] P = Callable[[mypy_extensions.VarArg(int)], int] # ok -Q = Callable[[Arg(int, type=int)], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: "Arg" gets multiple values for keyword argument "type" -R = Callable[[Arg(int, 'x', name='y')], int] # E: Invalid type alias: expression is not a valid type # E: Value of type "int" is not indexable # E: "Arg" gets multiple values for keyword argument "name" +Q = Callable[[Arg(int, type=int)], int] # E: Invalid type alias: expression is not a valid type \ + # E: Value of type "int" is not indexable \ + # E: "Arg" gets multiple values for keyword argument "type" +R = Callable[[Arg(int, 'x', name='y')], int] # E: Invalid type alias: expression is not a valid type \ + # E: Value of type "int" is not indexable \ + # E: "Arg" gets multiple values for keyword argument "name" + + + + + + + [builtins fixtures/dict.pyi] [case testCallableParsing] @@ -2250,13 +2281,26 @@ def dec(f: Callable[[A, str], None]) -> Callable[[A, int], None]: pass [out] [case testUnknownFunctionNotCallable] +from typing import TypeVar + def f() -> None: pass def g(x: int) -> None: pass h = f if bool() else g -reveal_type(h) # N: Revealed type is "builtins.function" -h(7) # E: Cannot call function of unknown type +reveal_type(h) # N: Revealed type is "Union[def (), def (x: builtins.int)]" +h(7) # E: Too many arguments for "f" + +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +h2 = join(f, g) +reveal_type(h2) # N: Revealed type is "builtins.function" +h2(7) # E: Cannot call function of unknown type + +h3 = join(g, f) +reveal_type(h3) # N: Revealed type is "builtins.function" +h3(7) # E: Cannot call function of unknown type [builtins fixtures/bool.pyi] [case testFunctionWithNameUnderscore] @@ -3382,3 +3426,40 @@ for factory in ( reveal_type(factory) # N: Revealed type is "def () -> Union[builtins.str, None]" var = factory() [builtins fixtures/tuple.pyi] + +[case testLambdaInDeferredDecoratorNoCrash] +def foo(x): + pass + +class Bar: + def baz(self, x): + pass + +class Qux(Bar): + @foo(lambda x: None) + def baz(self, x) -> None: + pass +[builtins fixtures/tuple.pyi] + +[case testGeneratorInDeferredDecoratorNoCrash] +from typing import Protocol, TypeVar + +T = TypeVar("T", covariant=True) + +class SupportsNext(Protocol[T]): + def __next__(self) -> T: ... + +def next(i: SupportsNext[T]) -> T: ... + +def foo(x): + pass + +class Bar: + def baz(self, x): + pass + +class Qux(Bar): + @next(f for f in [foo]) + def baz(self, x) -> None: + pass +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index e4b3e4cffdc11..c1868b3e3d729 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -191,6 +191,7 @@ functools.partial(1) # E: "int" not callable \ [case testFunctoolsPartialStar] import functools +from typing import List def foo(a: int, b: str, *args: int, d: str, **kwargs: int) -> int: ... @@ -214,7 +215,16 @@ def bar(*a: bytes, **k: int): p1("a", d="a", **k) p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**Dict[str, int]"; expected "str" p1(**k) # E: Argument 1 to "foo" has incompatible type "**Dict[str, int]"; expected "str" - p1(*a) # E: List or tuple expected as variadic arguments + p1(*a) # E: Expected iterable as variadic argument + + +def baz(a: int, b: int) -> int: ... +def test_baz(xs: List[int]): + p3 = functools.partial(baz, *xs) + p3() + p3(1) # E: Too many arguments for "baz" + + [builtins fixtures/dict.pyi] [case testFunctoolsPartialGeneric] @@ -338,15 +348,32 @@ fn1: Union[Callable[[int], int], Callable[[int], int]] reveal_type(functools.partial(fn1, 2)()) # N: Revealed type is "builtins.int" fn2: Union[Callable[[int], int], Callable[[int], str]] -reveal_type(functools.partial(fn2, 2)()) # N: Revealed type is "builtins.object" +reveal_type(functools.partial(fn2, 2)()) # N: Revealed type is "Union[builtins.int, builtins.str]" fn3: Union[Callable[[int], int], str] reveal_type(functools.partial(fn3, 2)()) # E: "str" not callable \ - # E: "Union[Callable[[int], int], str]" not callable \ # N: Revealed type is "builtins.int" \ # E: Argument 1 to "partial" has incompatible type "Union[Callable[[int], int], str]"; expected "Callable[..., int]" [builtins fixtures/tuple.pyi] +[case testFunctoolsPartialUnionOfTypeAndCallable] +import functools +from typing import Callable, Union, Type +from typing_extensions import TypeAlias + +class FooBar: + def __init__(self, arg1: str) -> None: + pass + +def f1(t: Union[Type[FooBar], Callable[..., 'FooBar']]) -> None: + val = functools.partial(t) + +FooBarFunc: TypeAlias = Callable[..., 'FooBar'] + +def f2(t: Union[Type[FooBar], FooBarFunc]) -> None: + val = functools.partial(t) +[builtins fixtures/tuple.pyi] + [case testFunctoolsPartialExplicitType] from functools import partial from typing import Type, TypeVar, Callable @@ -408,33 +435,83 @@ def foo(cls3: Type[B[T]]): from typing_extensions import TypedDict, Unpack from functools import partial -class Data(TypedDict, total=False): - x: int - -def f(**kwargs: Unpack[Data]) -> None: ... -def g(**kwargs: Unpack[Data]) -> None: - partial(f, **kwargs)() - -class MoreData(TypedDict, total=False): - x: int - y: int +class D1(TypedDict, total=False): + a1: int + +def fn1(a1: int) -> None: ... # N: "fn1" defined here +def main1(**d1: Unpack[D1]) -> None: + partial(fn1, **d1)() + partial(fn1, **d1)(**d1) + partial(fn1, **d1)(a1=1) + partial(fn1, **d1)(a1="asdf") # E: Argument "a1" to "fn1" has incompatible type "str"; expected "int" + partial(fn1, **d1)(oops=1) # E: Unexpected keyword argument "oops" for "fn1" + +def fn2(**kwargs: Unpack[D1]) -> None: ... # N: "fn2" defined here +def main2(**d1: Unpack[D1]) -> None: + partial(fn2, **d1)() + partial(fn2, **d1)(**d1) + partial(fn2, **d1)(a1=1) + partial(fn2, **d1)(a1="asdf") # E: Argument "a1" to "fn2" has incompatible type "str"; expected "int" + partial(fn2, **d1)(oops=1) # E: Unexpected keyword argument "oops" for "fn2" + +class D2(TypedDict, total=False): + a1: int + a2: str + +class A2Good(TypedDict, total=False): + a2: str +class A2Bad(TypedDict, total=False): + a2: int + +def fn3(a1: int, a2: str) -> None: ... # N: "fn3" defined here +def main3(a2good: A2Good, a2bad: A2Bad, **d2: Unpack[D2]) -> None: + partial(fn3, **d2)() + partial(fn3, **d2)(a1=1, a2="asdf") + + partial(fn3, **d2)(**d2) + + partial(fn3, **d2)(a1="asdf") # E: Argument "a1" to "fn3" has incompatible type "str"; expected "int" + partial(fn3, **d2)(a1=1, a2="asdf", oops=1) # E: Unexpected keyword argument "oops" for "fn3" + + partial(fn3, **d2)(**a2good) + partial(fn3, **d2)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str" + +def fn4(**kwargs: Unpack[D2]) -> None: ... # N: "fn4" defined here +def main4(a2good: A2Good, a2bad: A2Bad, **d2: Unpack[D2]) -> None: + partial(fn4, **d2)() + partial(fn4, **d2)(a1=1, a2="asdf") + + partial(fn4, **d2)(**d2) + + partial(fn4, **d2)(a1="asdf") # E: Argument "a1" to "fn4" has incompatible type "str"; expected "int" + partial(fn4, **d2)(a1=1, a2="asdf", oops=1) # E: Unexpected keyword argument "oops" for "fn4" + + partial(fn3, **d2)(**a2good) + partial(fn3, **d2)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str" + +def main5(**d2: Unpack[D2]) -> None: + partial(fn1, **d2)() # E: Extra argument "a2" from **args for "fn1" + partial(fn2, **d2)() # E: Extra argument "a2" from **args for "fn2" + +def main6(a2good: A2Good, a2bad: A2Bad, **d1: Unpack[D1]) -> None: + partial(fn3, **d1)() # E: Missing positional argument "a1" in call to "fn3" + partial(fn3, **d1)("asdf") # E: Too many positional arguments for "fn3" \ + # E: Too few arguments for "fn3" \ + # E: Argument 1 to "fn3" has incompatible type "str"; expected "int" + partial(fn3, **d1)(a2="asdf") + partial(fn3, **d1)(**a2good) + partial(fn3, **d1)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str" + + partial(fn4, **d1)() + partial(fn4, **d1)("asdf") # E: Too many positional arguments for "fn4" \ + # E: Argument 1 to "fn4" has incompatible type "str"; expected "int" + partial(fn4, **d1)(a2="asdf") + partial(fn4, **d1)(**a2good) + partial(fn4, **d1)(**a2bad) # E: Argument "a2" to "fn4" has incompatible type "int"; expected "str" -def f_more(**kwargs: Unpack[MoreData]) -> None: ... -def g_more(**kwargs: Unpack[MoreData]) -> None: - partial(f_more, **kwargs)() - -class Good(TypedDict, total=False): - y: int -class Bad(TypedDict, total=False): - y: str - -def h(**kwargs: Unpack[Data]) -> None: - bad: Bad - partial(f_more, **kwargs)(**bad) # E: Argument "y" to "f_more" has incompatible type "str"; expected "int" - good: Good - partial(f_more, **kwargs)(**good) [builtins fixtures/dict.pyi] + [case testFunctoolsPartialNestedGeneric] from functools import partial from typing import Generic, TypeVar, List @@ -456,6 +533,21 @@ first_kw([1]) # E: Too many positional arguments for "get" \ # E: Argument 1 to "get" has incompatible type "List[int]"; expected "int" [builtins fixtures/list.pyi] +[case testFunctoolsPartialHigherOrder] +from functools import partial +from typing import Callable + +def fn(a: int, b: str, c: bytes) -> int: ... + +def callback1(fn: Callable[[str, bytes], int]) -> None: ... +def callback2(fn: Callable[[str, int], int]) -> None: ... + +callback1(partial(fn, 1)) +# TODO: false negative +# https://github.com/python/mypy/issues/17461 +callback2(partial(fn, 1)) +[builtins fixtures/tuple.pyi] + [case testFunctoolsPartialClassObjectMatchingPartial] from functools import partial @@ -468,3 +560,82 @@ p(1, "no") # E: Argument 2 to "A" has incompatible type "str"; expected "int" q: partial[A] = partial(A, 1) # OK [builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypeVarBound] +from typing import Callable, TypeVar, Type +import functools + +T = TypeVar("T", bound=Callable[[str, int], str]) +S = TypeVar("S", bound=Type[int]) + +def foo(f: T) -> T: + g = functools.partial(f, "foo") + return f + +def bar(f: S) -> S: + g = functools.partial(f, "foo") + return f +[builtins fixtures/primitives.pyi] + +[case testFunctoolsPartialAbstractType] +# flags: --python-version 3.9 +from abc import ABC, abstractmethod +from functools import partial + +class A(ABC): + def __init__(self) -> None: ... + @abstractmethod + def method(self) -> None: ... + +def f1(cls: type[A]) -> None: + cls() + partial_cls = partial(cls) + partial_cls() + +def f2() -> None: + A() # E: Cannot instantiate abstract class "A" with abstract attribute "method" + partial_cls = partial(A) # E: Cannot instantiate abstract class "A" with abstract attribute "method" + partial_cls() # E: Cannot instantiate abstract class "A" with abstract attribute "method" +[builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialSelfType] +from functools import partial +from typing_extensions import Self + +class A: + def __init__(self, ts: float, msg: str) -> None: ... + + @classmethod + def from_msg(cls, msg: str) -> Self: + factory = partial(cls, ts=0) + return factory(msg=msg) +[builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypeVarValues] +from functools import partial +from typing import TypeVar + +T = TypeVar("T", int, str) + +def f(x: int, y: T) -> T: + return y + +def g(x: T, y: int) -> T: + return x + +def h(x: T, y: T) -> T: + return x + +fp = partial(f, 1) +reveal_type(fp(1)) # N: Revealed type is "builtins.int" +reveal_type(fp("a")) # N: Revealed type is "builtins.str" +fp(object()) # E: Value of type variable "T" of "f" cannot be "object" + +gp = partial(g, 1) +reveal_type(gp(1)) # N: Revealed type is "builtins.int" +gp("a") # E: Argument 1 to "g" has incompatible type "str"; expected "int" + +hp = partial(h, 1) +reveal_type(hp(1)) # N: Revealed type is "builtins.int" +hp("a") # E: Argument 1 to "h" has incompatible type "str"; expected "int" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 24292bce3e21d..888b4c26a7c70 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6586,6 +6586,7 @@ class TheClass: for x in names }) self.enum_type = pyenum +[builtins fixtures/tuple.pyi] [out] [out2] tmp/a.py:3: note: Revealed type is "def (value: builtins.object) -> lib.TheClass.pyenum@6" @@ -6726,3 +6727,20 @@ from typing_extensions import TypeIs def guard(x: object) -> TypeIs[int]: pass [builtins fixtures/tuple.pyi] + +[case testStartUsingPEP604Union] +# flags: --python-version 3.10 +import a +[file a.py] +import lib + +[file a.py.2] +from lib import IntOrStr +assert isinstance(1, IntOrStr) + +[file lib.py] +from typing_extensions import TypeAlias + +IntOrStr: TypeAlias = int | str +assert isinstance(1, IntOrStr) +[builtins fixtures/type.pyi] diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index afe6548df2d49..17ae6d9934b75 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -701,7 +701,7 @@ class A: pass class B(A): pass class C(A): pass def f(func: Callable[[T], S], *z: T, r: Optional[S] = None) -> S: pass -reveal_type(f(lambda x: 0 if isinstance(x, B) else 1)) # N: Revealed type is "builtins.int" +reveal_type(f(lambda x: 0 if isinstance(x, B) else 1)) # N: Revealed type is "Union[Literal[0]?, Literal[1]?]" f(lambda x: 0 if isinstance(x, B) else 1, A())() # E: "int" not callable f(lambda x: x if isinstance(x, B) else B(), A(), r=B())() # E: "B" not callable f( @@ -1391,7 +1391,7 @@ from typing import Union, List, Any def f(x: Union[List[str], Any]) -> None: a = x if x else [] - reveal_type(a) # N: Revealed type is "Union[builtins.list[Union[builtins.str, Any]], builtins.list[builtins.str], Any]" + reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.str], Any, builtins.list[Union[builtins.str, Any]]]" [builtins fixtures/list.pyi] [case testConditionalExpressionWithEmptyIteableAndUnionWithAny] @@ -1399,7 +1399,7 @@ from typing import Union, Iterable, Any def f(x: Union[Iterable[str], Any]) -> None: a = x if x else [] - reveal_type(a) # N: Revealed type is "Union[builtins.list[Union[builtins.str, Any]], typing.Iterable[builtins.str], Any]" + reveal_type(a) # N: Revealed type is "Union[typing.Iterable[builtins.str], Any, builtins.list[Union[builtins.str, Any]]]" [builtins fixtures/list.pyi] [case testInferMultipleAnyUnionCovariant] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index fcd03f8efe014..5a99e65c9c90a 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1238,6 +1238,35 @@ class B: pass [builtins fixtures/for.pyi] [out] +[case testForStatementIndexNarrowing] +from typing_extensions import TypedDict + +class X(TypedDict): + hourly: int + daily: int + +x: X +for a in ("hourly", "daily"): + reveal_type(a) # N: Revealed type is "Union[Literal['hourly']?, Literal['daily']?]" + reveal_type(x[a]) # N: Revealed type is "builtins.int" + reveal_type(a.upper()) # N: Revealed type is "builtins.str" + c = a + reveal_type(c) # N: Revealed type is "builtins.str" + a = "monthly" + reveal_type(a) # N: Revealed type is "builtins.str" + a = "yearly" + reveal_type(a) # N: Revealed type is "builtins.str" + a = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") + reveal_type(a) # N: Revealed type is "builtins.str" + d = a + reveal_type(d) # N: Revealed type is "builtins.str" + +b: str +for b in ("hourly", "daily"): + reveal_type(b) # N: Revealed type is "builtins.str" + reveal_type(b.upper()) # N: Revealed type is "builtins.str" +[builtins fixtures/for.pyi] + -- Regression tests -- ---------------- @@ -1438,18 +1467,22 @@ class Wrapper: def f(cond: bool) -> Any: f = Wrapper if cond else lambda x: x - reveal_type(f) # N: Revealed type is "def (x: Any) -> Any" + reveal_type(f) # N: Revealed type is "Union[def (x: Any) -> __main__.Wrapper, def (x: Any) -> Any]" return f(3) def g(cond: bool) -> Any: f = lambda x: x if cond else Wrapper - reveal_type(f) # N: Revealed type is "def (x: Any) -> Any" + reveal_type(f) # N: Revealed type is "def (x: Any) -> Union[Any, def (x: Any) -> __main__.Wrapper]" + return f(3) + +def h(cond: bool) -> Any: + f = (lambda x: x) if cond else Wrapper + reveal_type(f) # N: Revealed type is "Union[def (x: Any) -> Any, def (x: Any) -> __main__.Wrapper]" return f(3) -- Boolean operators -- ----------------- - [case testOrOperationWithGenericOperands] from typing import List a: List[A] @@ -3644,7 +3677,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(__x: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[str], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[NamedArg(Never, 'x')], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsPosOnly] @@ -3660,7 +3694,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(*, x: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[NamedArg(str, 'x')], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[Never], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallablePosOnlyVsKwargs] @@ -3676,7 +3711,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(**x: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[KwArg(str)], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[Never], None]" [builtins fixtures/list.pyi] [case testCallableInferenceAgainstCallableNamedVsArgs] @@ -3692,7 +3728,8 @@ def f(x: Call[T]) -> Tuple[T, T]: ... def g(*args: str) -> None: pass reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \ - # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" + # E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]" \ + # N: "Call[Never].__call__" has type "Callable[[NamedArg(Never, 'x')], None]" [builtins fixtures/list.pyi] [case testInferenceAgainstTypeVarActualBound] diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index b7ee38b69d00f..99bd62765b11d 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1833,6 +1833,30 @@ def f(x: T) -> None: reveal_type(x) # N: Revealed type is "T`-1" [builtins fixtures/isinstance.pyi] +[case testIsinstanceAndNegativeNarrowTypeVariableWithUnionBound] +from typing import Union, TypeVar + +class A: + a: int +class B: + b: int + +T = TypeVar("T", bound=Union[A, B]) + +def f(x: T) -> T: + if isinstance(x, A): + reveal_type(x) # N: Revealed type is "__main__.A" + x.a + x.b # E: "A" has no attribute "b" + else: + reveal_type(x) # N: Revealed type is "T`-1" + x.a # E: "T" has no attribute "a" + x.b + x.a # E: Item "B" of the upper bound "Union[A, B]" of type variable "T" has no attribute "a" + x.b # E: Item "A" of the upper bound "Union[A, B]" of type variable "T" has no attribute "b" + return x +[builtins fixtures/isinstance.pyi] + [case testIsinstanceAndTypeType] from typing import Type def f(x: Type[int]) -> None: @@ -2183,23 +2207,24 @@ def foo2(x: Optional[str]) -> None: reveal_type(x) # N: Revealed type is "builtins.str" [builtins fixtures/isinstance.pyi] -[case testNoneCheckDoesNotNarrowWhenUsingTypeVars] - -# Note: this test (and the following one) are testing checker.conditional_type_map: -# if you set the 'prohibit_none_typevar_overlap' keyword argument to False when calling -# 'is_overlapping_types', the binder will incorrectly infer that 'out' has a type of -# Union[T, None] after the if statement. - +[case testNoneCheckDoesNotMakeTypeVarOptional] from typing import TypeVar T = TypeVar('T') -def foo(x: T) -> T: +def foo_if(x: T) -> T: out = None out = x if out is None: pass return out + +def foo_while(x: T) -> T: + out = None + out = x + while out is None: + pass + return out [builtins fixtures/isinstance.pyi] [case testNoneCheckDoesNotNarrowWhenUsingTypeVarsNoStrictOptional] diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test index 4beac047e278b..3a8c7f5ba4545 100644 --- a/test-data/unit/check-kwargs.test +++ b/test-data/unit/check-kwargs.test @@ -358,6 +358,9 @@ f(**d) # E: Keywords must be strings f(**A()) # E: Argument after ** must be a mapping, not "A" kwargs: Optional[Any] f(**kwargs) # E: Argument after ** must be a mapping, not "Optional[Any]" + +def g(a: int) -> None: pass +g(a=1, **4) # E: Argument after ** must be a mapping, not "int" [builtins fixtures/dict.pyi] [case testPassingKeywordVarArgsToNonVarArgsFunction] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 8f8aaf6a3982a..2f94b5df0f83c 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -608,36 +608,35 @@ e: Literal[dummy()] # E: Invalid type: Literal[...] cannot contain a [case testLiteralDisallowCollections] from typing_extensions import Literal -a: Literal[{"a": 1, "b": 2}] # E: Invalid type: Literal[...] cannot contain arbitrary expressions +a: Literal[{"a": 1, "b": 2}] # E: Parameter 1 of Literal[...] is invalid b: Literal[{1, 2, 3}] # E: Invalid type: Literal[...] cannot contain arbitrary expressions -c: {"a": 1, "b": 2} # E: Invalid type comment or annotation +c: {"a": 1, "b": 2} # E: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict \ + # E: Invalid type: try using Literal[1] instead? \ + # E: Invalid type: try using Literal[2] instead? d: {1, 2, 3} # E: Invalid type comment or annotation [builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] [case testLiteralDisallowCollections2] - from typing_extensions import Literal a: (1, 2, 3) # E: Syntax error in type annotation \ # N: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) b: Literal[[1, 2, 3]] # E: Parameter 1 of Literal[...] is invalid c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid as a type [builtins fixtures/tuple.pyi] -[out] [case testLiteralDisallowCollectionsTypeAlias] - from typing_extensions import Literal -at = Literal[{"a": 1, "b": 2}] # E: Invalid type alias: expression is not a valid type +at = Literal[{"a": 1, "b": 2}] # E: Parameter 1 of Literal[...] is invalid bt = {"a": 1, "b": 2} -a: at # E: Variable "__main__.at" is not valid as a type \ - # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +a: at +reveal_type(a) # N: Revealed type is "Any" b: bt # E: Variable "__main__.bt" is not valid as a type \ # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases [builtins fixtures/dict.pyi] -[out] +[typing fixtures/typing-typeddict.pyi] [case testLiteralDisallowCollectionsTypeAlias2] - from typing_extensions import Literal at = Literal[{1, 2, 3}] # E: Invalid type alias: expression is not a valid type bt = {1, 2, 3} @@ -2961,3 +2960,27 @@ class C(B[Literal["word"]]): reveal_type(C().collection) # N: Revealed type is "builtins.list[Literal['word']]" reveal_type(C().word) # N: Revealed type is "Literal['word']" [builtins fixtures/tuple.pyi] + +[case testLiteralTernaryUnionNarrowing] +from typing_extensions import Literal +from typing import Optional + +SEP = Literal["a", "b"] + +class Base: + def feed_data( + self, + sep: SEP, + ) -> int: + return 0 + +class C(Base): + def feed_data( + self, + sep: Optional[SEP] = None, + ) -> int: + if sep is None: + sep = "a" if int() else "b" + reveal_type(sep) # N: Revealed type is "Union[Literal['a'], Literal['b']]" + return super().feed_data(sep) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 5fd48577e436e..68897790e4bf0 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -3136,15 +3136,12 @@ import google.cloud.ndb # E: Library stubs not installed for "google.cloud.ndb" from google.cloud import ndb [case testMissingSubmoduleOfInstalledStubPackage] -import bleach.xyz -from bleach.abc import fgh +import bleach.exists +import bleach.xyz # E: Cannot find implementation or library stub for module named "bleach.xyz" \ + # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +from bleach.abc import fgh # E: Cannot find implementation or library stub for module named "bleach.abc" [file bleach/__init__.pyi] -[out] -main:1: error: Library stubs not installed for "bleach.xyz" -main:1: note: Hint: "python3 -m pip install types-bleach" -main:1: note: (or run "mypy --install-types" to install all missing stub packages) -main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -main:2: error: Library stubs not installed for "bleach.abc" +[file bleach/exists.pyi] [case testMissingSubmoduleOfInstalledStubPackageIgnored] # flags: --ignore-missing-imports diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index e9d156754d9cc..df2c7ffc80672 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -824,20 +824,14 @@ class Fraction(Real): [builtins fixtures/tuple.pyi] [case testForwardReferenceInNamedTuple] -from typing import List, NamedTuple +from typing import NamedTuple class A(NamedTuple): b: 'B' x: int - y: List['B'] class B: pass - -def f(a: A): - reveal_type(a.b) # N: Revealed type is "__main__.B" - reveal_type(a.x) # N: Revealed type is "builtins.int" - reveal_type(a.y) # N: Revealed type is "builtins.list[__main__.B]" [builtins fixtures/tuple.pyi] [case testTypeNamedTupleClassmethod] @@ -1423,3 +1417,27 @@ class Foo(typing.NamedTuple): reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testNameErrorInNamedTupleNestedInFunction1] +from typing import NamedTuple + +def bar() -> None: + class MyNamedTuple(NamedTuple): + a: int + def foo(self) -> None: + ... + int_set: Set[int] # E: Name "Set" is not defined \ + # N: Did you forget to import it from "typing"? (Suggestion: "from typing import Set") +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] + +[case testNameErrorInNamedTupleNestedInFunction2] +from typing import NamedTuple + +def bar() -> None: + class MyNamedTuple(NamedTuple): + a: int + def foo(self) -> None: + misspelled_var_name # E: Name "misspelled_var_name" is not defined +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-namedtuple.pyi] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 8612df9bc6635..285d56ff7e50a 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1007,7 +1007,7 @@ str_or_false: Union[Literal[False], str] if str_or_false: reveal_type(str_or_false) # N: Revealed type is "builtins.str" else: - reveal_type(str_or_false) # N: Revealed type is "Union[Literal[False], builtins.str]" + reveal_type(str_or_false) # N: Revealed type is "Union[Literal[False], Literal['']]" true_or_false: Literal[True, False] @@ -1017,6 +1017,22 @@ else: reveal_type(true_or_false) # N: Revealed type is "Literal[False]" [builtins fixtures/primitives.pyi] +[case testNarrowingFalseyToLiteral] +from typing import Union + +a: str +b: bytes +c: int +d: Union[str, bytes, int] + +if not a: + reveal_type(a) # N: Revealed type is "Literal['']" +if not b: + reveal_type(b) # N: Revealed type is "Literal[b'']" +if not c: + reveal_type(c) # N: Revealed type is "Literal[0]" +if not d: + reveal_type(d) # N: Revealed type is "Union[Literal[''], Literal[b''], Literal[0]]" [case testNarrowingIsInstanceFinalSubclass] # flags: --warn-unreachable @@ -1369,9 +1385,9 @@ val: Optional[A] if val == None: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" else: - reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" + reveal_type(val) # N: Revealed type is "__main__.A" if val != None: - reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" + reveal_type(val) # N: Revealed type is "__main__.A" else: reveal_type(val) # N: Revealed type is "Union[__main__.A, None]" @@ -2114,3 +2130,225 @@ else: [typing fixtures/typing-medium.pyi] [builtins fixtures/ops.pyi] + +[case testNarrowingWithIntEnum] +# mypy: strict-equality +from __future__ import annotations +from typing import Any +from enum import IntEnum + +class IE(IntEnum): + X = 1 + Y = 2 + +def f1(x: int) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + reveal_type(x) # N: Revealed type is "builtins.int" + if x != IE.X: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2(x: IE) -> None: + if x == 1: + reveal_type(x) # N: Revealed type is "__main__.IE" + else: + reveal_type(x) # N: Revealed type is "__main__.IE" + +def f3(x: object) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "builtins.object" + else: + reveal_type(x) # N: Revealed type is "builtins.object" + +def f4(x: int | Any) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + else: + reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" + +def f5(x: int) -> None: + if x is IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "builtins.int" + if x is not IE.X: + reveal_type(x) # N: Revealed type is "builtins.int" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + +def f6(x: IE) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y]" +[builtins fixtures/primitives.pyi] + +[case testNarrowingWithIntEnum2] +# mypy: strict-equality +from __future__ import annotations +from typing import Any +from enum import IntEnum, Enum + +class MyDecimal: ... + +class IE(IntEnum): + X = 1 + Y = 2 + +class IE2(IntEnum): + X = 1 + Y = 2 + +class E(Enum): + X = 1 + Y = 2 + +def f1(x: IE | MyDecimal) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.MyDecimal]" + else: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.MyDecimal]" + +def f2(x: E | bytes) -> None: + if x == E.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.E.Y], builtins.bytes]" + +def f3(x: IE | IE2) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.IE2]" + else: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, __main__.IE2]" + +def f4(x: IE | E) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + elif x == E.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.IE.Y], Literal[__main__.E.Y]]" + +def f5(x: E | str | int) -> None: + if x == E.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.E.Y], builtins.str, builtins.int]" + +def f6(x: IE | Any) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, Any]" + else: + reveal_type(x) # N: Revealed type is "Union[__main__.IE, Any]" + +def f7(x: IE | None) -> None: + if x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "Union[Literal[__main__.IE.Y], None]" + +def f8(x: IE | None) -> None: + if x is None: + reveal_type(x) # N: Revealed type is "None" + elif x == IE.X: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y]" +[builtins fixtures/primitives.pyi] + +[case testNarrowingWithStrEnum] +# mypy: strict-equality +from enum import StrEnum + +class SE(StrEnum): + A = 'a' + B = 'b' + +def f1(x: str) -> None: + if x == SE.A: + reveal_type(x) # N: Revealed type is "builtins.str" + else: + reveal_type(x) # N: Revealed type is "builtins.str" + +def f2(x: SE) -> None: + if x == 'a': + reveal_type(x) # N: Revealed type is "__main__.SE" + else: + reveal_type(x) # N: Revealed type is "__main__.SE" + +def f3(x: object) -> None: + if x == SE.A: + reveal_type(x) # N: Revealed type is "builtins.object" + else: + reveal_type(x) # N: Revealed type is "builtins.object" + +def f4(x: SE) -> None: + if x == SE.A: + reveal_type(x) # N: Revealed type is "Literal[__main__.SE.A]" + else: + reveal_type(x) # N: Revealed type is "Literal[__main__.SE.B]" +[builtins fixtures/primitives.pyi] + +[case testConsistentNarrowingEqAndIn] +# flags: --python-version 3.10 + +# https://github.com/python/mypy/issues/17864 +def f(x: str | int) -> None: + if x == "x": + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + y = x + + if x in ["x"]: + # TODO: we should fix this reveal https://github.com/python/mypy/issues/3229 + reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" + y = x + z = x + z = y +[builtins fixtures/primitives.pyi] + +[case testConsistentNarrowingInWithCustomEq] +# flags: --python-version 3.10 + +# https://github.com/python/mypy/issues/17864 +class C: + def __init__(self, x: int) -> None: + self.x = x + + def __eq__(self, other: object) -> bool: + raise + # Example implementation: + # if isinstance(other, C) and other.x == self.x: + # return True + # return NotImplemented + +class D(C): + pass + +def f(x: C) -> None: + if x in [D(5)]: + reveal_type(x) # D # N: Revealed type is "__main__.C" + +f(C(5)) +[builtins fixtures/primitives.pyi] + +[case testNarrowingTypeVarNone] +# flags: --warn-unreachable + +# https://github.com/python/mypy/issues/18126 +from typing import TypeVar + +T = TypeVar("T") + +def fn_if(arg: T) -> None: + if arg is None: + return None + return None + +def fn_while(arg: T) -> None: + while arg is None: + return None + return None +[builtins fixtures/primitives.pyi] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 511c7b003015f..784b9db9f66e5 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -1908,9 +1908,9 @@ else: @dec def f(x: int) -> None: 1() # E: "int" not callable -reveal_type(f) # N: Revealed type is "def (x: builtins.str)" +reveal_type(f) # N: Revealed type is "def (builtins.str)" [file m.py] -def f(x: str) -> None: pass +def f(x: str, /) -> None: pass [case testNewAnalyzerConditionallyDefineFuncOverVar] from typing import Callable @@ -2069,6 +2069,7 @@ from enum import Enum A = Enum('A', ['x', 'y']) A = Enum('A', ['z', 't']) # E: Name "A" already defined on line 3 +[builtins fixtures/tuple.pyi] [case testNewAnalyzerNewTypeRedefinition] from typing import NewType diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 70f3c4486e14d..683ce04469152 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -53,14 +53,14 @@ x = None # type: Optional[int] if x: reveal_type(x) # N: Revealed type is "builtins.int" else: - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(x) # N: Revealed type is "Union[Literal[0], None]" [builtins fixtures/bool.pyi] [case testIfNotCases] from typing import Optional x = None # type: Optional[int] if not x: - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(x) # N: Revealed type is "Union[Literal[0], None]" else: reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/bool.pyi] @@ -109,13 +109,13 @@ reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" from typing import Optional x = None # type: Optional[str] y1 = x and 'b' -reveal_type(y1) # N: Revealed type is "Union[builtins.str, None]" +reveal_type(y1) # N: Revealed type is "Union[Literal[''], None, builtins.str]" y2 = x and 1 # x could be '', so... -reveal_type(y2) # N: Revealed type is "Union[builtins.str, None, builtins.int]" +reveal_type(y2) # N: Revealed type is "Union[Literal[''], None, builtins.int]" z1 = 'b' and x reveal_type(z1) # N: Revealed type is "Union[builtins.str, None]" z2 = int() and x -reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" +reveal_type(z2) # N: Revealed type is "Union[Literal[0], builtins.str, None]" [case testLambdaReturningNone] f = lambda: None @@ -395,7 +395,7 @@ def lookup_field(name, obj): attr = None [case testTernaryWithNone] -reveal_type(None if bool() else 0) # N: Revealed type is "Union[Literal[0]?, None]" +reveal_type(None if bool() else 0) # N: Revealed type is "Union[None, Literal[0]?]" [builtins fixtures/bool.pyi] [case testListWithNone] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 48d5996b226f3..9d01ce6bd4803 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6463,7 +6463,7 @@ class D: ... def f1(g: A) -> A: ... if True: @overload # E: Single overload definition, multiple required - def f1(g: B) -> B: ... + def f1(g: B) -> B: ... # E: Incompatible redefinition (redefinition with type "Callable[[B], B]", original type "Callable[[A], A]") if maybe_true: # E: Condition can't be inferred, unable to merge overloads \ # E: Name "maybe_true" is not defined @overload @@ -6480,14 +6480,14 @@ if True: def f2(g: B) -> B: ... elif maybe_true: # E: Name "maybe_true" is not defined @overload # E: Single overload definition, multiple required - def f2(g: C) -> C: ... + def f2(g: C) -> C: ... # E: Incompatible redefinition (redefinition with type "Callable[[C], C]", original type "Callable[[A], A]") def f2(g): ... # E: Name "f2" already defined on line 21 @overload # E: Single overload definition, multiple required def f3(g: A) -> A: ... if True: @overload # E: Single overload definition, multiple required - def f3(g: B) -> B: ... + def f3(g: B) -> B: ... # E: Incompatible redefinition (redefinition with type "Callable[[B], B]", original type "Callable[[A], A]") if True: pass # Some other node @overload # E: Name "f3" already defined on line 32 \ @@ -6750,3 +6750,21 @@ def foo(x: object) -> str: ... def bar(x: int) -> int: ... @overload def bar(x: Any) -> str: ... + +[case testOverloadOnInvalidTypeArgument] +from typing import TypeVar, Self, Generic, overload + +class C: pass + +T = TypeVar("T", bound=C) + +class D(Generic[T]): + @overload + def f(self, x: int) -> int: ... + @overload + def f(self, x: str) -> str: ... + def f(Self, x): ... + +a: D[str] # E: Type argument "str" of "D" must be a subtype of "C" +reveal_type(a.f(1)) # N: Revealed type is "builtins.int" +reveal_type(a.f("x")) # N: Revealed type is "builtins.str" diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index e6d8cec3f0b04..7f038b8117419 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1193,28 +1193,7 @@ def func(callback: Callable[P, str]) -> Callable[P, str]: return inner [builtins fixtures/paramspec.pyi] -[case testParamSpecArgsAndKwargsStringified] -from typing import Callable -from typing_extensions import ParamSpec - -P1 = ParamSpec("P1") - -def func(callback: Callable[P1, str]) -> Callable[P1, str]: - def inner(*args: "P1.args", **kwargs: "P1.kwargs") -> str: - return "foo" - return inner - -@func -def outer(a: int) -> str: - return "" - -outer(1) # OK -outer("x") # E: Argument 1 to "outer" has incompatible type "str"; expected "int" -outer(a=1) # OK -outer(b=1) # E: Unexpected keyword argument "b" for "outer" -[builtins fixtures/paramspec.pyi] - -[case testParamSpecArgsAndKwargsMismatch] +[case testParamSpecArgsAndKwargsMissmatch] from typing import Callable from typing_extensions import ParamSpec @@ -1473,6 +1452,17 @@ y: C[int, str] reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.int, builtins.str) -> builtins.int" [builtins fixtures/paramspec.pyi] +[case testParamSpecInTypeAliasIllegalBare] +from typing import ParamSpec +from typing_extensions import Concatenate, TypeAlias + +P = ParamSpec("P") +Bad1: TypeAlias = P # E: Invalid location for ParamSpec "P" \ + # N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +Bad2: TypeAlias = Concatenate[int, P] # E: Invalid location for Concatenate \ + # N: You can use Concatenate as the first argument to Callable +[builtins fixtures/paramspec.pyi] + [case testParamSpecInTypeAliasRecursive] from typing import ParamSpec, Callable, Union @@ -1857,6 +1847,15 @@ c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed reveal_type(c) # N: Revealed type is "__main__.C[Any]" [builtins fixtures/paramspec.pyi] +[case testParamSpecInheritNoCrashOnNested] +from typing import Generic +from typing_extensions import ParamSpec + +P = ParamSpec("P") +class C(Generic[P]): ... +class D(C[int, [int, str], str]): ... # E: Nested parameter specifications are not allowed +[builtins fixtures/paramspec.pyi] + [case testParamSpecConcatenateSelfType] from typing import Callable from typing_extensions import ParamSpec, Concatenate @@ -2204,3 +2203,273 @@ parametrize(_test, Case(1, b=2), Case(3, b=4)) parametrize(_test, Case(1, 2), Case(3)) parametrize(_test, Case(1, 2), Case(3, b=4)) [builtins fixtures/paramspec.pyi] + +[case testRunParamSpecInsufficientArgs] +from typing_extensions import ParamSpec, Concatenate +from typing import Callable + +_P = ParamSpec("_P") + +def run(predicate: Callable[_P, None], *args: _P.args, **kwargs: _P.kwargs) -> None: # N: "run" defined here + predicate() # E: Too few arguments + predicate(*args) # E: Too few arguments + predicate(**kwargs) # E: Too few arguments + predicate(*args, **kwargs) + +def fn() -> None: ... +def fn_args(x: int) -> None: ... +def fn_posonly(x: int, /) -> None: ... + +run(fn) +run(fn_args, 1) +run(fn_args, x=1) +run(fn_posonly, 1) +run(fn_posonly, x=1) # E: Unexpected keyword argument "x" for "run" + +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecConcatenateInsufficientArgs] +from typing_extensions import ParamSpec, Concatenate +from typing import Callable + +_P = ParamSpec("_P") + +def run(predicate: Callable[Concatenate[int, _P], None], *args: _P.args, **kwargs: _P.kwargs) -> None: # N: "run" defined here + predicate() # E: Too few arguments + predicate(1) # E: Too few arguments + predicate(1, *args) # E: Too few arguments + predicate(1, *args) # E: Too few arguments + predicate(1, **kwargs) # E: Too few arguments + predicate(*args, **kwargs) # E: Argument 1 has incompatible type "*_P.args"; expected "int" + predicate(1, *args, **kwargs) + +def fn() -> None: ... +def fn_args(x: int, y: str) -> None: ... +def fn_posonly(x: int, /) -> None: ... +def fn_posonly_args(x: int, /, y: str) -> None: ... + +run(fn) # E: Argument 1 to "run" has incompatible type "Callable[[], None]"; expected "Callable[[int], None]" +run(fn_args, 1, 'a') # E: Too many arguments for "run" \ + # E: Argument 2 to "run" has incompatible type "int"; expected "str" +run(fn_args, y='a') +run(fn_args, 'a') +run(fn_posonly) +run(fn_posonly, x=1) # E: Unexpected keyword argument "x" for "run" +run(fn_posonly_args) # E: Missing positional argument "y" in call to "run" +run(fn_posonly_args, 'a') +run(fn_posonly_args, y='a') + +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecConcatenateInsufficientArgsInDecorator] +from typing_extensions import ParamSpec, Concatenate +from typing import Callable + +P = ParamSpec("P") + +def decorator(fn: Callable[Concatenate[str, P], None]) -> Callable[P, None]: + def inner(*args: P.args, **kwargs: P.kwargs) -> None: + fn("value") # E: Too few arguments + fn("value", *args) # E: Too few arguments + fn("value", **kwargs) # E: Too few arguments + fn(*args, **kwargs) # E: Argument 1 has incompatible type "*P.args"; expected "str" + fn("value", *args, **kwargs) + return inner + +@decorator +def foo(s: str, s2: str) -> None: ... + +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecOverload] +from typing_extensions import ParamSpec +from typing import Callable, NoReturn, TypeVar, Union, overload + +P = ParamSpec("P") +T = TypeVar("T") + +@overload +def capture( + sync_fn: Callable[P, NoReturn], + *args: P.args, + **kwargs: P.kwargs, +) -> int: ... +@overload +def capture( + sync_fn: Callable[P, T], + *args: P.args, + **kwargs: P.kwargs, +) -> Union[T, int]: ... +def capture( + sync_fn: Callable[P, T], + *args: P.args, + **kwargs: P.kwargs, +) -> Union[T, int]: + return sync_fn(*args, **kwargs) + +def fn() -> str: return '' +def err() -> NoReturn: ... + +reveal_type(capture(fn)) # N: Revealed type is "Union[builtins.str, builtins.int]" +reveal_type(capture(err)) # N: Revealed type is "builtins.int" + +[builtins fixtures/paramspec.pyi] + +[case testRunParamSpecOverlappingOverloadsOrder] +from typing import Any, Callable, overload +from typing_extensions import ParamSpec + +P = ParamSpec("P") + +class Base: + pass +class Child(Base): + def __call__(self) -> str: ... +class NotChild: + def __call__(self) -> str: ... + +@overload +def handle(func: Base) -> int: ... +@overload +def handle(func: Callable[P, str], *args: P.args, **kwargs: P.kwargs) -> str: ... +def handle(func: Any, *args: Any, **kwargs: Any) -> Any: + return func(*args, **kwargs) + +@overload +def handle_reversed(func: Callable[P, str], *args: P.args, **kwargs: P.kwargs) -> str: ... +@overload +def handle_reversed(func: Base) -> int: ... +def handle_reversed(func: Any, *args: Any, **kwargs: Any) -> Any: + return func(*args, **kwargs) + +reveal_type(handle(Child())) # N: Revealed type is "builtins.int" +reveal_type(handle(NotChild())) # N: Revealed type is "builtins.str" + +reveal_type(handle_reversed(Child())) # N: Revealed type is "builtins.str" +reveal_type(handle_reversed(NotChild())) # N: Revealed type is "builtins.str" + +[builtins fixtures/paramspec.pyi] + +[case testBindPartial] +from functools import partial +from typing_extensions import ParamSpec +from typing import Callable, TypeVar + +P = ParamSpec("P") +T = TypeVar("T") + +def run(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + return func2(*args) + +def run2(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args) + return func2(**kwargs) + +def run3(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args, **kwargs) + return func2() + +def run4(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args, **kwargs) + return func2(**kwargs) + +def run_bad(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args, **kwargs) + return func2(*args) # E: Too many arguments + +def run_bad2(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + return func2(**kwargs) # E: Too few arguments + +def run_bad3(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args) + return func2() # E: Too few arguments + +[builtins fixtures/paramspec.pyi] + +[case testBindPartialConcatenate] +from functools import partial +from typing_extensions import Concatenate, ParamSpec +from typing import Callable, TypeVar + +P = ParamSpec("P") +T = TypeVar("T") + +def run(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, **kwargs) + return func2(*args) + +def run2(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + p = [""] + func2(1, *p) # E: Too few arguments \ + # E: Argument 2 has incompatible type "*List[str]"; expected "P.args" + func2(1, 2, *p) # E: Too few arguments \ + # E: Argument 2 has incompatible type "int"; expected "P.args" \ + # E: Argument 3 has incompatible type "*List[str]"; expected "P.args" + func2(1, *args, *p) # E: Argument 3 has incompatible type "*List[str]"; expected "P.args" + func2(1, *p, *args) # E: Argument 2 has incompatible type "*List[str]"; expected "P.args" + return func2(1, *args) + +def run3(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, *args) + d = {"":""} + func2(**d) # E: Too few arguments \ + # E: Argument 1 has incompatible type "**Dict[str, str]"; expected "P.kwargs" + return func2(**kwargs) + +def run4(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1) + return func2(*args, **kwargs) + +def run5(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, *args, **kwargs) + func2() + return func2(**kwargs) + +def run_bad(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, *args) # E: Argument 1 has incompatible type "*P.args"; expected "int" + return func2(1, **kwargs) # E: Argument 1 has incompatible type "int"; expected "P.args" + +def run_bad2(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, *args) + func2() # E: Too few arguments + func2(*args, **kwargs) # E: Too many arguments + return func2(1, **kwargs) # E: Argument 1 has incompatible type "int"; expected "P.args" + +def run_bad3(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1, **kwargs) + func2() # E: Too few arguments + return func2(1, *args) # E: Argument 1 has incompatible type "int"; expected "P.args" + +def run_bad4(func: Callable[Concatenate[int, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, 1) + func2() # E: Too few arguments + func2(*args) # E: Too few arguments + func2(1, *args) # E: Too few arguments \ + # E: Argument 1 has incompatible type "int"; expected "P.args" + func2(1, **kwargs) # E: Too few arguments \ + # E: Argument 1 has incompatible type "int"; expected "P.args" + return func2(**kwargs) # E: Too few arguments + +[builtins fixtures/paramspec.pyi] + +[case testOtherVarArgs] +from functools import partial +from typing_extensions import Concatenate, ParamSpec +from typing import Callable, TypeVar, Tuple + +P = ParamSpec("P") +T = TypeVar("T") + +def run(func: Callable[Concatenate[int, str, P], T], *args: P.args, **kwargs: P.kwargs) -> T: + func2 = partial(func, **kwargs) + args_prefix: Tuple[int, str] = (1, 'a') + func2(*args_prefix) # E: Too few arguments + func2(*args, *args_prefix) # E: Argument 1 has incompatible type "*P.args"; expected "int" \ + # E: Argument 1 has incompatible type "*P.args"; expected "str" \ + # E: Argument 2 has incompatible type "*Tuple[int, str]"; expected "P.args" + return func2(*args_prefix, *args) + +[builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index b96c00730a748..0c653d6081875 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2475,3 +2475,23 @@ class B: reveal_type(B.__hash__) # N: Revealed type is "None" [builtins fixtures/plugin_attrs.pyi] + +[case testAttrsStrictOptionalSetProperly] +from typing import Generic, Optional, TypeVar + +import attr + +T = TypeVar("T") + +@attr.mutable() +class Parent(Generic[T]): + run_type: Optional[int] = None + +@attr.mutable() +class Child(Parent[float]): + pass + +Parent(run_type = None) +c = Child(run_type = None) +reveal_type(c.run_type) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index ee7556461fd33..dd19eb1f21d63 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2473,7 +2473,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int, VarArg(str)], None]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int, VarArg(str)], None]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(str, 'x'), VarArg(int)], None]" [builtins fixtures/tuple.pyi] [out] @@ -2510,7 +2511,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int], int]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int], int]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(T, 'x')], T]" [builtins fixtures/tuple.pyi] [out] @@ -2530,7 +2532,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[T], Tuple[T, T]]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[T], Tuple[T, T]]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(int, 'x')], int]" [builtins fixtures/tuple.pyi] [out] @@ -2557,7 +2560,8 @@ def func(caller: Caller) -> None: pass func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[Union[int, str]], Union[int, str]]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[Union[int, str]], Union[int, str]]"; expected "Caller" \ + # N: "Caller.__call__" has type overloaded function [out] [case testCallableImplementsProtocolExtraNote] @@ -2596,7 +2600,8 @@ def anon(caller: CallerAnon) -> None: func(call) -func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[str], None]"; expected "Caller" +func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[str], None]"; expected "Caller" \ + # N: "Caller.__call__" has type "Callable[[Arg(str, 'x')], None]" anon(bad) [out] @@ -2619,7 +2624,8 @@ a: Other b: Bad func(a) -func(b) # E: Argument 1 to "func" has incompatible type "Bad"; expected "One" +func(b) # E: Argument 1 to "func" has incompatible type "Bad"; expected "One" \ + # N: "One.__call__" has type "Callable[[Arg(str, 'x')], None]" [out] [case testJoinProtocolCallback] @@ -3589,7 +3595,8 @@ test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" # N: Expected: \ # N: def __call__(x: int, y: int) -> Any \ # N: Got: \ - # N: def __init__(x: int, y: str) -> C + # N: def __init__(x: int, y: str) -> C \ + # N: "P.__call__" has type "Callable[[Arg(int, 'x'), Arg(int, 'y')], Any]" [case testProtocolClassObjectPureCallback] from typing import Any, ClassVar, Protocol @@ -3610,7 +3617,8 @@ test(C) # E: Argument 1 to "test" has incompatible type "Type[C]"; expected "P" # N: Expected: \ # N: def __call__(x: int, y: int) -> Any \ # N: Got: \ - # N: def __init__(x: int, y: str) -> C + # N: def __init__(x: int, y: str) -> C \ + # N: "P.__call__" has type "Callable[[Arg(int, 'x'), Arg(int, 'y')], Any]" [builtins fixtures/type.pyi] [case testProtocolClassObjectCallableError] @@ -4127,3 +4135,112 @@ class P(Protocol): class C(P): ... C(0) # OK + +[case testTypeVarValueConstraintAgainstGenericProtocol] +from typing import TypeVar, Generic, Protocol, overload + +T_contra = TypeVar("T_contra", contravariant=True) +AnyStr = TypeVar("AnyStr", str, bytes) + +class SupportsWrite(Protocol[T_contra]): + def write(self, s: T_contra, /) -> None: ... + +class Buffer: ... + +class IO(Generic[AnyStr]): + @overload + def write(self: IO[bytes], s: Buffer, /) -> None: ... + @overload + def write(self, s: AnyStr, /) -> None: ... + def write(self, s): ... + +def foo(fdst: SupportsWrite[AnyStr]) -> None: ... + +x: IO[str] +foo(x) + +[case testTypeVarValueConstraintAgainstGenericProtocol2] +from typing import Generic, Protocol, TypeVar, overload + +AnyStr = TypeVar("AnyStr", str, bytes) +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + +class SupportsRead(Generic[T_co]): + def read(self) -> T_co: ... + +class SupportsWrite(Protocol[T_contra]): + def write(self, s: T_contra) -> object: ... + +def copyfileobj(fsrc: SupportsRead[AnyStr], fdst: SupportsWrite[AnyStr]) -> None: ... + +class WriteToMe(Generic[AnyStr]): + @overload + def write(self: WriteToMe[str], s: str) -> int: ... + @overload + def write(self: WriteToMe[bytes], s: bytes) -> int: ... + def write(self, s): ... + +class WriteToMeOrReadFromMe(WriteToMe[AnyStr], SupportsRead[AnyStr]): ... + +copyfileobj(WriteToMeOrReadFromMe[bytes](), WriteToMe[bytes]()) + +[case testOverloadedMethodWithExplictSelfTypes] +from typing import Generic, overload, Protocol, TypeVar, Union + +AnyStr = TypeVar("AnyStr", str, bytes) +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + +class SupportsRead(Protocol[T_co]): + def read(self) -> T_co: ... + +class SupportsWrite(Protocol[T_contra]): + def write(self, s: T_contra) -> int: ... + +class Input(Generic[AnyStr]): + def read(self) -> AnyStr: ... + +class Output(Generic[AnyStr]): + @overload + def write(self: Output[str], s: str) -> int: ... + @overload + def write(self: Output[bytes], s: bytes) -> int: ... + def write(self, s: Union[str, bytes]) -> int: ... + +def f(src: SupportsRead[AnyStr], dst: SupportsWrite[AnyStr]) -> None: ... + +def g1(a: Input[bytes], b: Output[bytes]) -> None: + f(a, b) + +def g2(a: Input[bytes], b: Output[bytes]) -> None: + f(a, b) + +def g3(a: Input[str], b: Output[bytes]) -> None: + f(a, b) # E: Cannot infer type argument 1 of "f" + +def g4(a: Input[bytes], b: Output[str]) -> None: + f(a, b) # E: Cannot infer type argument 1 of "f" + +[builtins fixtures/tuple.pyi] + +[case testOverloadProtocolSubtyping] +from typing import Protocol, Self, overload + +class NumpyFloat: + __add__: "FloatOP" + +class FloatOP(Protocol): + @overload + def __call__(self, other: float) -> NumpyFloat: ... + @overload + def __call__(self, other: NumpyFloat) -> NumpyFloat: ... + +class SupportsAdd(Protocol): + @overload + def __add__(self, other: float) -> Self: ... + @overload + def __add__(self, other: NumpyFloat) -> Self: ... + +x: SupportsAdd = NumpyFloat() +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 5ecc69dc7c324..0231b47cf4a0e 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -648,6 +648,25 @@ match m: reveal_type(m) # N: Revealed type is "builtins.tuple[Any, ...]" [builtins fixtures/primitives.pyi] +[case testMatchClassPatternCaptureSelfSubtype] +class A(str): + pass + +class B(str): + __match_args__ = ("b",) + b: int + +def f1(x: A): + match x: + case A(a): + reveal_type(a) # N: Revealed type is "__main__.A" + +def f2(x: B): + match x: + case B(b): + reveal_type(b) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + [case testMatchInvalidClassPattern] m: object @@ -1424,6 +1443,7 @@ def f(value: Literal[1] | Literal[2]) -> int: [case testMatchSequencePatternNegativeNarrowing] from typing import Union, Sequence, Tuple +from typing_extensions import Literal m1: Sequence[int | str] @@ -1448,6 +1468,31 @@ match m3: reveal_type(m3) # N: Revealed type is "Tuple[Literal[1]]" case r2: reveal_type(m3) # N: Revealed type is "Tuple[Union[builtins.int, builtins.str]]" + +m4: Tuple[Literal[1], int] + +match m4: + case (1, 5): + reveal_type(m4) # N: Revealed type is "Tuple[Literal[1], Literal[5]]" + case (1, 6): + reveal_type(m4) # N: Revealed type is "Tuple[Literal[1], Literal[6]]" + case _: + reveal_type(m4) # N: Revealed type is "Tuple[Literal[1], builtins.int]" + +m5: Tuple[Literal[1, 2], Literal["a", "b"]] + +match m5: + case (1, str()): + reveal_type(m5) # N: Revealed type is "Tuple[Literal[1], Union[Literal['a'], Literal['b']]]" + case _: + reveal_type(m5) # N: Revealed type is "Tuple[Literal[2], Union[Literal['a'], Literal['b']]]" + +match m5: + case (1, "a"): + reveal_type(m5) # N: Revealed type is "Tuple[Literal[1], Literal['a']]" + case _: + reveal_type(m5) # N: Revealed type is "Tuple[Union[Literal[1], Literal[2]], Union[Literal['a'], Literal['b']]]" + [builtins fixtures/tuple.pyi] [case testMatchEnumSingleChoice] @@ -1498,6 +1543,62 @@ def g(m: Medal) -> int: case Medal.bronze: reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" return 2 +[builtins fixtures/enum.pyi] + + +[case testMatchLiteralPatternEnumWithTypedAttribute] +from enum import Enum +from typing import NoReturn +def assert_never(x: NoReturn) -> None: ... + +class int: + def __new__(cls, value: int): pass + +class Medal(int, Enum): + prize: str + + def __new__(cls, value: int, prize: str) -> Medal: + enum = int.__new__(cls, value) + enum._value_ = value + enum.prize = prize + return enum + + gold = (1, 'cash prize') + silver = (2, 'sponsorship') + bronze = (3, 'nothing') + +m: Medal + +match m: + case Medal.gold: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" + case Medal.silver: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]" + case Medal.bronze: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" + case _ as unreachable: + assert_never(unreachable) + +[builtins fixtures/tuple.pyi] + +[case testMatchLiteralPatternFunctionalEnum] +from enum import Enum +from typing import NoReturn +def assert_never(x: NoReturn) -> None: ... + +Medal = Enum('Medal', 'gold silver bronze') +m: Medal + +match m: + case Medal.gold: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" + case Medal.silver: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]" + case Medal.bronze: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" + case _ as unreachable: + assert_never(unreachable) +[builtins fixtures/enum.pyi] [case testMatchLiteralPatternEnumCustomEquals-skip] from enum import Enum @@ -1515,6 +1616,7 @@ match m: reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" case _: reveal_type(m) # N: Revealed type is "__main__.Medal" +[builtins fixtures/enum.pyi] [case testMatchNarrowUsingPatternGuardSpecialCase] def f(x: int | str) -> int: @@ -1592,6 +1694,7 @@ def union(x: str | bool) -> None: case True: return reveal_type(x) # N: Revealed type is "Union[builtins.str, Literal[False]]" +[builtins fixtures/tuple.pyi] [case testMatchAssertFalseToSilenceFalsePositives] class C: @@ -1622,6 +1725,22 @@ def f(x: int | str) -> int: case str() as s: return 1 +[case testMatchOrPatternExhaustiveness] +from typing import NoReturn, Literal +def assert_never(x: NoReturn) -> None: ... + +Color = Literal["blue", "green", "red"] +c: Color + +match c: + case "blue": + reveal_type(c) # N: Revealed type is "Literal['blue']" + case "green" | "notColor": + reveal_type(c) # N: Revealed type is "Literal['green']" + case _: + assert_never(c) # E: Argument 1 to "assert_never" has incompatible type "Literal['red']"; expected "Never" +[typing fixtures/typing-typeddict.pyi] + [case testMatchAsPatternIntersection-skip] class A: pass class B: pass @@ -2263,3 +2382,60 @@ def test(xs: Tuple[Unpack[Ts]]) -> None: reveal_type(b3) # N: Revealed type is "builtins.list[builtins.object]" reveal_type(c3) # N: Revealed type is "builtins.int" [builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternTypeVarBoundNoCrash] +# This was crashing: https://github.com/python/mypy/issues/18089 +from typing import TypeVar, Sequence, Any + +T = TypeVar("T", bound=Sequence[Any]) + +def f(x: T) -> None: + match x: + case [_]: + pass +[builtins fixtures/tuple.pyi] + +[case testMatchSequencePatternTypeVarBoundNarrows] +from typing import TypeVar, Sequence + +T = TypeVar("T", bound=Sequence[int | str]) + +def accept_seq_int(x: Sequence[int]): ... + +def f(x: T) -> None: + match x: + case [1, 2]: + accept_seq_int(x) + case _: + accept_seq_int(x) # E: Argument 1 to "accept_seq_int" has incompatible type "T"; expected "Sequence[int]" +[builtins fixtures/tuple.pyi] + +[case testNarrowingTypeVarMatch] +# flags: --warn-unreachable + +# https://github.com/python/mypy/issues/18126 +from typing import TypeVar + +T = TypeVar("T") + +def fn_case(arg: T) -> None: + match arg: + case None: + return None + return None +[builtins fixtures/primitives.pyi] + +[case testNoneCheckDoesNotMakeTypeVarOptionalMatch] +from typing import TypeVar + +T = TypeVar('T') + +def foo(x: T) -> T: + out = None + out = x + match out: + case None: + pass + return out + +[builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 28951824999f8..6f4c540572b00 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -173,3 +173,89 @@ Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1 reveal_type(x4) # N: Revealed type is "def (*Any) -> builtins.int" [builtins fixtures/tuple.pyi] + +[case testReturnInExceptStarBlock1] +# flags: --python-version 3.11 +def foo() -> None: + try: + pass + except* Exception: + return # E: "return" not allowed in except* block + finally: + return +[builtins fixtures/exception.pyi] + +[case testReturnInExceptStarBlock2] +# flags: --python-version 3.11 +def foo(): + while True: + try: + pass + except* Exception: + while True: + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testContinueInExceptBlockNestedInExceptStarBlock] +# flags: --python-version 3.11 +while True: + try: + ... + except* Exception: + try: + ... + except Exception: + continue # E: "continue" not allowed in except* block + continue # E: "continue" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testReturnInExceptBlockNestedInExceptStarBlock] +# flags: --python-version 3.11 +def foo(): + try: + ... + except* Exception: + try: + ... + except Exception: + return # E: "return" not allowed in except* block + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testBreakContinueReturnInExceptStarBlock1] +# flags: --python-version 3.11 +from typing import Iterable +def foo(x: Iterable[int]) -> None: + for _ in x: + try: + pass + except* Exception: + continue # E: "continue" not allowed in except* block + except* Exception: + for _ in x: + continue + break # E: "break" not allowed in except* block + except* Exception: + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] + +[case testBreakContinueReturnInExceptStarBlock2] +# flags: --python-version 3.11 +def foo(): + while True: + try: + pass + except* Exception: + def inner(): + while True: + if 1 < 1: + continue + else: + break + return + if 1 < 2: + break # E: "break" not allowed in except* block + if 1 < 2: + continue # E: "continue" not allowed in except* block + return # E: "return" not allowed in except* block +[builtins fixtures/exception.pyi] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 27027d30a684a..8b4d638ecdaae 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -1,70 +1,56 @@ -[case test695TypeAlias] -type MyInt = int # E: PEP 695 type aliases are not yet supported +[case testPEP695TypeAliasBasic] +type MyInt = int def f(x: MyInt) -> MyInt: return reveal_type(x) # N: Revealed type is "builtins.int" -type MyList[T] = list[T] # E: PEP 695 type aliases are not yet supported \ - # E: Name "T" is not defined +type MyList[T] = list[T] -def g(x: MyList[int]) -> MyList[int]: # E: Variable "__main__.MyList" is not valid as a type \ - # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases - return reveal_type(x) # N: Revealed type is "MyList?[builtins.int]" +def g(x: MyList[int]) -> MyList[int]: + return reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" -type MyInt2 = int # type: ignore[valid-type] +type MyInt2 = int def h(x: MyInt2) -> MyInt2: return reveal_type(x) # N: Revealed type is "builtins.int" -[case test695Class] -class MyGen[T]: # E: PEP 695 generics are not yet supported - def __init__(self, x: T) -> None: # E: Name "T" is not defined +[case testPEP695Class] +class MyGen[T]: + def __init__(self, x: T) -> None: self.x = x -def f(x: MyGen[int]): # E: "MyGen" expects no type arguments, but 1 given - reveal_type(x.x) # N: Revealed type is "Any" +def f(x: MyGen[int]): + reveal_type(x.x) # N: Revealed type is "builtins.int" -[case test695Function] -def f[T](x: T) -> T: # E: PEP 695 generics are not yet supported \ - # E: Name "T" is not defined - return reveal_type(x) # N: Revealed type is "Any" +[case testPEP695Function] +def f[T](x: T) -> T: + return reveal_type(x) # N: Revealed type is "T`-1" -reveal_type(f(1)) # N: Revealed type is "Any" +reveal_type(f(1)) # N: Revealed type is "builtins.int" -async def g[T](x: T) -> T: # E: PEP 695 generics are not yet supported \ - # E: Name "T" is not defined - return reveal_type(x) # N: Revealed type is "Any" +async def g[T](x: T) -> T: + return reveal_type(x) # N: Revealed type is "T`-1" -reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, Any]" must be used \ +reveal_type(g(1)) # E: Value of type "Coroutine[Any, Any, int]" must be used \ # N: Are you missing an await? \ - # N: Revealed type is "typing.Coroutine[Any, Any, Any]" + # N: Revealed type is "typing.Coroutine[Any, Any, builtins.int]" -[case test695TypeVar] +[case testPEP695TypeVarBasic] from typing import Callable -type Alias1[T: int] = list[T] # E: PEP 695 type aliases are not yet supported \ - # E: Name "T" is not defined -type Alias2[**P] = Callable[P, int] # E: PEP 695 type aliases are not yet supported \ - # E: Value of type "int" is not indexable \ - # E: Name "P" is not defined -type Alias3[*Ts] = tuple[*Ts] # E: PEP 695 type aliases are not yet supported \ - # E: Name "Ts" is not defined - -class Cls1[T: int]: ... # E: PEP 695 generics are not yet supported -class Cls2[**P]: ... # E: PEP 695 generics are not yet supported -class Cls3[*Ts]: ... # E: PEP 695 generics are not yet supported - -def func1[T: int](x: T) -> T: ... # E: PEP 695 generics are not yet supported \ - # E: Name "T" is not defined - -def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... # E: PEP 695 generics are not yet supported \ - # E: The first argument to Callable must be a list of types, parameter specification, or "..." \ - # N: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas \ - # E: Name "P" is not defined -def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... # E: PEP 695 generics are not yet supported \ - # E: Name "Ts" is not defined +type Alias1[T: int] = list[T] +type Alias2[**P] = Callable[P, int] +type Alias3[*Ts] = tuple[*Ts] + +class Cls1[T: int]: ... +class Cls2[**P]: ... +class Cls3[*Ts]: ... + +def func1[T: int](x: T) -> T: ... +def func2[**P](x: Callable[P, int]) -> Callable[P, str]: ... +def func3[*Ts](x: tuple[*Ts]) -> tuple[int, *Ts]: ... [builtins fixtures/tuple.pyi] -[case test695TypeAliasType] +[case testPEP695TypeAliasType] from typing import Callable, TypeAliasType, TypeVar, TypeVarTuple T = TypeVar("T") @@ -86,9 +72,13 @@ reveal_type(ba2) # N: Revealed type is "def (*Any) -> builtins.str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] -[case testPEP695GenericFunctionSyntax] -# flags: --enable-incomplete-feature=NewGenericSyntax +[case testPEP695IncompleteFeatureIsAcceptedButHasNoEffect] +# mypy: enable-incomplete-feature=NewGenericSyntax +def f[T](x: T) -> T: + return x +reveal_type(f(1)) # N: Revealed type is "builtins.int" +[case testPEP695GenericFunctionSyntax] def ident[TV](x: TV) -> TV: y: TV = x y = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "TV") @@ -107,8 +97,6 @@ reveal_type(tup(1, 'x')) # N: Revealed type is "Tuple[builtins.int, builtins.st [builtins fixtures/tuple.pyi] [case testPEP695GenericClassSyntax] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T]: x: T @@ -128,8 +116,6 @@ reveal_type(c.x) # N: Revealed type is "builtins.int" reveal_type(c.ident(1)) # N: Revealed type is "builtins.int" [case testPEP695GenericMethodInGenericClass] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T]: def m[S](self, x: S) -> T | S: ... @@ -139,8 +125,6 @@ b: C[object] = C[int]() reveal_type(C[str]().m(1)) # N: Revealed type is "Union[builtins.str, builtins.int]" [case testPEP695InferVarianceSimpleFromMethod] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant[T]: def f(self, x: T) -> None: pass @@ -178,8 +162,6 @@ if int(): f = e [case testPEP695InferVarianceSimpleFromAttribute] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant1[T]: def __init__(self, x: T) -> None: self.x = x @@ -214,8 +196,6 @@ if int(): b3 = a3 # E: Incompatible types in assignment (expression has type "Invariant3[object]", variable has type "Invariant3[int]") [case testPEP695InferVarianceRecursive] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant[T]: def f(self, x: Invariant[T]) -> Invariant[T]: return x @@ -233,7 +213,7 @@ b: Invariant[int] if int(): a = b # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[object]") if int(): - b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") + b = a c: Covariant[object] d: Covariant[int] @@ -249,9 +229,25 @@ if int(): if int(): f = e -[case testPEP695InferVarianceCalculateOnDemand] -# flags: --enable-incomplete-feature=NewGenericSyntax +[case testPEP695InferVarianceInFrozenDataclass] +from dataclasses import dataclass +@dataclass(frozen=True) +class Covariant[T]: + x: T + +cov1: Covariant[float] = Covariant[int](1) +cov2: Covariant[int] = Covariant[float](1) # E: Incompatible types in assignment (expression has type "Covariant[float]", variable has type "Covariant[int]") + +@dataclass(frozen=True) +class Invariant[T]: + x: list[T] + +inv1: Invariant[float] = Invariant[int]([1]) # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[float]") +inv2: Invariant[int] = Invariant[float]([1]) # E: Incompatible types in assignment (expression has type "Invariant[float]", variable has type "Invariant[int]") +[builtins fixtures/tuple.pyi] + +[case testPEP695InferVarianceCalculateOnDemand] class Covariant[T]: def __init__(self) -> None: self.x = [1] @@ -267,8 +263,6 @@ class Covariant[T]: def h(self, x: Covariant[int]) -> None: pass [case testPEP695InferVarianceNotReadyWhenNeeded] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Covariant[T]: def f(self) -> None: c = Covariant[int]() @@ -309,8 +303,6 @@ if int(): b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]") [case testPEP695InferVarianceNotReadyForJoin] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Invariant[T]: def f(self) -> None: # Assume covariance if variance us not ready @@ -323,8 +315,6 @@ class Invariant[T]: reveal_type([Invariant(1), Invariant(object())]) # N: Revealed type is "builtins.list[builtins.object]" [case testPEP695InferVarianceNotReadyForMeet] -# flags: --enable-incomplete-feature=NewGenericSyntax - from typing import TypeVar, Callable S = TypeVar("S") @@ -342,9 +332,124 @@ class Invariant[T]: reveal_type(c(a1, a2)) # N: Revealed type is "Never" -[case testPEP695InheritInvariant] -# flags: --enable-incomplete-feature=NewGenericSyntax +[case testPEP695InferVarianceUnderscorePrefix] +class Covariant1[T]: + def __init__(self, x: T) -> None: + self._x = x + + @property + def x(self) -> T: + return self._x + +co1_1: Covariant1[float] = Covariant1[int](1) +co1_2: Covariant1[int] = Covariant1[float](1) # E: Incompatible types in assignment (expression has type "Covariant1[float]", variable has type "Covariant1[int]") + +class Covariant2[T]: + def __init__(self, x: T) -> None: + self.__foo_bar = x + + @property + def x(self) -> T: + return self.__foo_bar + +co2_1: Covariant2[float] = Covariant2[int](1) +co2_2: Covariant2[int] = Covariant2[float](1) # E: Incompatible types in assignment (expression has type "Covariant2[float]", variable has type "Covariant2[int]") + +class Invariant1[T]: + def __init__(self, x: T) -> None: + self._x = x + + # Methods behave differently from attributes + def _f(self, x: T) -> None: ... + + @property + def x(self) -> T: + return self._x + +inv1_1: Invariant1[float] = Invariant1[int](1) # E: Incompatible types in assignment (expression has type "Invariant1[int]", variable has type "Invariant1[float]") +inv1_2: Invariant1[int] = Invariant1[float](1) # E: Incompatible types in assignment (expression has type "Invariant1[float]", variable has type "Invariant1[int]") + +class Invariant2[T]: + def __init__(self, x: T) -> None: + # Dunders are special + self.__x__ = x + + @property + def x(self) -> T: + return self.__x__ + +inv2_1: Invariant2[float] = Invariant2[int](1) # E: Incompatible types in assignment (expression has type "Invariant2[int]", variable has type "Invariant2[float]") +inv2_2: Invariant2[int] = Invariant2[float](1) # E: Incompatible types in assignment (expression has type "Invariant2[float]", variable has type "Invariant2[int]") + +class Invariant3[T]: + def __init__(self, x: T) -> None: + self._x = Invariant1(x) + @property + def x(self) -> T: + return self._x._x + +inv3_1: Invariant3[float] = Invariant3[int](1) # E: Incompatible types in assignment (expression has type "Invariant3[int]", variable has type "Invariant3[float]") +inv3_2: Invariant3[int] = Invariant3[float](1) # E: Incompatible types in assignment (expression has type "Invariant3[float]", variable has type "Invariant3[int]") +[builtins fixtures/property.pyi] + +[case testPEP695InferVarianceWithInheritedSelf] +from typing import overload, Self, TypeVar, Generic + +T = TypeVar("T") +S = TypeVar("S") + +class C(Generic[T]): + def f(self, x: T) -> Self: ... + def g(self) -> T: ... + +class D[T1, T2](C[T1]): + def m(self, x: T2) -> None: ... + +a1: D[int, int] = D[int, object]() +a2: D[int, object] = D[int, int]() # E: Incompatible types in assignment (expression has type "D[int, int]", variable has type "D[int, object]") +a3: D[int, int] = D[object, object]() # E: Incompatible types in assignment (expression has type "D[object, object]", variable has type "D[int, int]") +a4: D[object, int] = D[int, object]() # E: Incompatible types in assignment (expression has type "D[int, object]", variable has type "D[object, int]") + +[case testPEP695InferVarianceWithReturnSelf] +from typing import Self, overload + +class Cov[T]: + def f(self) -> Self: ... + +a1: Cov[int] = Cov[float]() # E: Incompatible types in assignment (expression has type "Cov[float]", variable has type "Cov[int]") +a2: Cov[float] = Cov[int]() + +class Contra[T]: + def f(self) -> Self: ... + def g(self, x: T) -> None: ... + +b1: Contra[int] = Contra[float]() +b2: Contra[float] = Contra[int]() # E: Incompatible types in assignment (expression has type "Contra[int]", variable has type "Contra[float]") + +class Cov2[T]: + @overload + def f(self, x): ... + @overload + def f(self) -> Self: ... + def f(self, x=None): ... + +c1: Cov2[int] = Cov2[float]() # E: Incompatible types in assignment (expression has type "Cov2[float]", variable has type "Cov2[int]") +c2: Cov2[float] = Cov2[int]() + +class Contra2[T]: + @overload + def f(self, x): ... + @overload + def f(self) -> Self: ... + def f(self, x=None): ... + + def g(self, x: T) -> None: ... + +d1: Contra2[int] = Contra2[float]() +d2: Contra2[float] = Contra2[int]() # E: Incompatible types in assignment (expression has type "Contra2[int]", variable has type "Contra2[float]") + +[case testPEP695InheritInvariant] class Invariant[T]: x: T @@ -366,7 +471,6 @@ if int(): b = a # E: Incompatible types in assignment (expression has type "Subclass[int]", variable has type "Subclass[object]") [case testPEP695InheritanceMakesInvariant] -# flags: --enable-incomplete-feature=NewGenericSyntax class Covariant[T]: def f(self) -> T: ... @@ -381,7 +485,6 @@ a: Subclass[int] = Subclass[object]() # E: Incompatible types in assignment (ex b: Subclass[object] = Subclass[int]() # E: Incompatible types in assignment (expression has type "Subclass[int]", variable has type "Subclass[object]") [case testPEP695InheritCoOrContravariant] -# flags: --enable-incomplete-feature=NewGenericSyntax class Contravariant[T]: def f(self, x: T) -> None: pass @@ -407,7 +510,6 @@ e: InvSubclass[int] = InvSubclass[object]() # E: Incompatible types in assignme f: InvSubclass[object] = InvSubclass[int]() # E: Incompatible types in assignment (expression has type "InvSubclass[int]", variable has type "InvSubclass[object]") [case testPEP695FinalAttribute] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Final class C[T]: @@ -418,8 +520,6 @@ a: C[int] = C[object](1) # E: Incompatible types in assignment (expression has b: C[object] = C[int](1) [case testPEP695TwoTypeVariables] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T, S]: def f(self, x: T) -> None: ... def g(self) -> S: ... @@ -430,8 +530,6 @@ c: C[int, int] = C[int, object]() # E: Incompatible types in assignment (expres d: C[int, object] = C[int, int]() [case testPEP695Properties] -# flags: --enable-incomplete-feature=NewGenericSyntax - class R[T]: @property def p(self) -> T: ... @@ -449,7 +547,6 @@ d: RW[object] = RW[int]() # E: Incompatible types in assignment (expression has [builtins fixtures/property.pyi] [case testPEP695Protocol] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Protocol class PContra[T](Protocol): @@ -486,8 +583,6 @@ if int(): f = e # E: Incompatible types in assignment (expression has type "PInv[int]", variable has type "PInv[object]") [case testPEP695TypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T]: pass class D[T, S]: pass @@ -509,15 +604,45 @@ a4: A4 reveal_type(a4) # N: Revealed type is "Union[builtins.int, builtins.str]" [builtins fixtures/type.pyi] +[case testPEP695TypeAliasNotValidAsBaseClass] +from typing import TypeAlias + +import m + +type A1 = int +class Bad1(A1): # E: Type alias defined using "type" statement not valid as base class + pass + +type A2[T] = list[T] +class Bad2(A2[int]): # E: Type alias defined using "type" statement not valid as base class + pass + +class Bad3(m.A1): # E: Type alias defined using "type" statement not valid as base class + pass + +class Bad4(m.A2[int]): # E: Type alias defined using "type" statement not valid as base class + pass + +B1 = int +B2 = list +B3: TypeAlias = int +class Good1(B1): pass +class Good2(B2[int]): pass +class Good3(list[A1]): pass +class Good4(list[A2[int]]): pass +class Good5(B3): pass + +[file m.py] +type A1 = str +type A2[T] = list[T] +[typing fixtures/typing-medium.pyi] + [case testPEP695TypeAliasWithUnusedTypeParams] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T] = int a: A[str] reveal_type(a) # N: Revealed type is "builtins.int" [case testPEP695TypeAliasForwardReference1] -# flags: --enable-incomplete-feature=NewGenericSyntax - type A[T] = C[T] a: A[int] @@ -526,8 +651,6 @@ reveal_type(a) # N: Revealed type is "__main__.C[builtins.int]" class C[T]: pass [case testPEP695TypeAliasForwardReference2] -# flags: --enable-incomplete-feature=NewGenericSyntax - type X = C type A = X @@ -539,8 +662,6 @@ class C: pass [typing fixtures/typing-full.pyi] [case testPEP695TypeAliasForwardReference3] -# flags: --enable-incomplete-feature=NewGenericSyntax - type X = D type A = C[X] @@ -551,13 +672,9 @@ class C[T]: pass class D: pass [case testPEP695TypeAliasForwardReference4] -# flags: --enable-incomplete-feature=NewGenericSyntax - type A = C -# Note that this doesn't actually work at runtime, but we currently don't -# keep track whether a type alias is valid in various runtime type contexts. -class D(A): +class D(A): # E: Type alias defined using "type" statement not valid as base class pass class C: pass @@ -566,7 +683,6 @@ x: C = D() y: D = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") [case testPEP695TypeAliasForwardReference5] -# flags: --enable-incomplete-feature=NewGenericSyntax type A = str type B[T] = C[T] class C[T]: pass @@ -578,15 +694,12 @@ reveal_type(b) # N: Revealed type is "__main__.C[builtins.int]" reveal_type(c) # N: Revealed type is "__main__.C[builtins.str]" [case testPEP695TypeAliasWithUndefineName] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T] = XXX # E: Name "XXX" is not defined a: A[int] reveal_type(a) # N: Revealed type is "Any" [case testPEP695TypeAliasInvalidType] -# flags: --enable-incomplete-feature=NewGenericSyntax -type A = int | 1 # E: Invalid type: try using Literal[1] instead? \ - # E: Unsupported operand types for | ("Type[int]" and "int") +type A = int | 1 # E: Invalid type: try using Literal[1] instead? a: A reveal_type(a) # N: Revealed type is "Union[builtins.int, Any]" @@ -596,13 +709,10 @@ reveal_type(b) # N: Revealed type is "Any" [builtins fixtures/type.pyi] [case testPEP695TypeAliasBoundForwardReference] -# mypy: enable-incomplete-feature=NewGenericSyntax type B[T: Foo] = list[T] class Foo: pass [case testPEP695UpperBound] -# flags: --enable-incomplete-feature=NewGenericSyntax - class D: x: int class E(D): pass @@ -625,8 +735,6 @@ reveal_type(f(E())) # N: Revealed type is "__main__.E" f(1) # E: Value of type variable "T" of "f" cannot be "int" [case testPEP695UpperBoundForwardReference1] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T: D]: pass a: C[D] @@ -640,8 +748,6 @@ class D: pass class E(D): pass [case testPEP695UpperBoundForwardReference2] -# flags: --enable-incomplete-feature=NewGenericSyntax - type A = D class C[T: A]: pass @@ -656,8 +762,6 @@ reveal_type(b) # N: Revealed type is "__main__.C[__main__.E]" c: C[int] # E: Type argument "int" of "C" must be a subtype of "D" [case testPEP695UpperBoundForwardReference3] -# flags: --enable-incomplete-feature=NewGenericSyntax - class D[T]: pass class E[T](D[T]): pass @@ -675,8 +779,6 @@ reveal_type(b) # N: Revealed type is "__main__.C[__main__.E[__main__.X]]" c: C[D[int]] # E: Type argument "D[int]" of "C" must be a subtype of "D[X]" [case testPEP695UpperBoundForwardReference4] -# flags: --enable-incomplete-feature=NewGenericSyntax - def f[T: D](a: T) -> T: reveal_type(a.x) # N: Revealed type is "builtins.int" return a @@ -690,8 +792,6 @@ reveal_type(f(E())) # N: Revealed type is "__main__.E" f(1) # E: Value of type variable "T" of "f" cannot be "int" [case testPEP695UpperBoundUndefinedName] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T: XX]: # E: Name "XX" is not defined pass @@ -702,8 +802,6 @@ def f[T: YY](x: T) -> T: # E: Name "YY" is not defined reveal_type(f) # N: Revealed type is "def [T <: Any] (x: T`-1) -> T`-1" [case testPEP695UpperBoundWithMultipleParams] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C[T, S: int]: pass class D[A: int, B]: pass @@ -721,8 +819,6 @@ f('x', None) # E: Value of type variable "T" of "f" cannot be "str" \ # E: Value of type variable "S" of "f" cannot be "None" [case testPEP695InferVarianceOfTupleType] -# flags: --enable-incomplete-feature=NewGenericSyntax - class Cov[T](tuple[int, str]): def f(self) -> T: pass @@ -742,9 +838,7 @@ e: Contra[int] = Contra[object]() f: Contra[object] = Contra[int]() # E: Incompatible types in assignment (expression has type "Contra[int]", variable has type "Contra[object]") [builtins fixtures/tuple-simple.pyi] -[case testPEP695ValueRestiction] -# flags: --enable-incomplete-feature=NewGenericSyntax - +[case testPEP695ValueRestriction] def f[T: (int, str)](x: T) -> T: reveal_type(x) # N: Revealed type is "builtins.int" \ # N: Revealed type is "builtins.str" @@ -760,9 +854,7 @@ a: C[object] b: C[None] c: C[int] # E: Value of type variable "T" of "C" cannot be "int" -[case testPEP695ValueRestictionForwardReference] -# flags: --enable-incomplete-feature=NewGenericSyntax - +[case testPEP695ValueRestrictionForwardReference] class C[T: (int, D)]: def __init__(self, x: T) -> None: a = x @@ -778,9 +870,7 @@ class D: pass C(D()) -[case testPEP695ValueRestictionUndefinedName] -# flags: --enable-incomplete-feature=NewGenericSyntax - +[case testPEP695ValueRestrictionUndefinedName] class C[T: (int, XX)]: # E: Name "XX" is not defined pass @@ -788,7 +878,6 @@ def f[S: (int, YY)](x: S) -> S: # E: Name "YY" is not defined return x [case testPEP695ParamSpec] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable def g[**P](f: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: @@ -810,7 +899,6 @@ reveal_type(a.m) # N: Revealed type is "def (builtins.int, builtins.str)" [builtins fixtures/tuple.pyi] [case testPEP695ParamSpecTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable type C[**P] = Callable[P, int] @@ -821,8 +909,6 @@ reveal_type(f) # N: Revealed type is "def (builtins.str, Union[builtins.int, No [typing fixtures/typing-full.pyi] [case testPEP695TypeVarTuple] -# flags: --enable-incomplete-feature=NewGenericSyntax - def f[*Ts](t: tuple[*Ts]) -> tuple[*Ts]: reveal_type(t) # N: Revealed type is "Tuple[Unpack[Ts`-1]]" return t @@ -842,7 +928,6 @@ b = c # E: Incompatible types in assignment (expression has type "C[str]", vari [builtins fixtures/tuple.pyi] [case testPEP695TypeVarTupleAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Callable type C[*Ts] = tuple[*Ts, int] @@ -852,7 +937,6 @@ reveal_type(a) # N: Revealed type is "Tuple[builtins.str, None, builtins.int]" [builtins fixtures/tuple.pyi] [case testPEP695IncrementalFunction] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -879,7 +963,6 @@ tmp/a.py:4: error: Value of type variable "T" of "g" cannot be "str" tmp/a.py:5: error: Value of type variable "S" of "g" cannot be "int" [case testPEP695IncrementalClass] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -912,7 +995,6 @@ tmp/a.py:12: error: Value of type variable "S" of "D" cannot be "SS" tmp/a.py:13: error: Type argument "object" of "D" must be a subtype of "int" [case testPEP695IncrementalParamSpecAndTypeVarTuple] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -936,7 +1018,6 @@ class D[**P]: tmp/a.py:6: note: Revealed type is "def (builtins.int, builtins.str)" [case testPEP695IncrementalTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -960,8 +1041,6 @@ tmp/a.py:3: note: Revealed type is "builtins.str" tmp/a.py:5: note: Revealed type is "b.Foo[builtins.int]" [case testPEP695UndefinedNameInGenericFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax - def f[T](x: T) -> T: return unknown() # E: Name "unknown" is not defined @@ -970,7 +1049,6 @@ class C: return unknown() # E: Name "unknown" is not defined [case testPEP695FunctionTypeVarAccessInFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import cast class C: @@ -982,8 +1060,6 @@ class C: reveal_type(C().m(1)) # N: Revealed type is "builtins.int" [case testPEP695ScopingBasics] -# mypy: enable-incomplete-feature=NewGenericSyntax - T = 1 def f[T](x: T) -> T: @@ -1000,8 +1076,6 @@ class C[T]: reveal_type(T) # N: Revealed type is "builtins.int" [case testPEP695ClassScoping] -# mypy: enable-incomplete-feature=NewGenericSyntax - class C: class D: pass @@ -1012,7 +1086,6 @@ C().m(C.D(), C.D()) C().m(1, C.D()) # E: Value of type variable "T" of "m" of "C" cannot be "int" [case testPEP695NestedGenericFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax def f[T](x: T) -> T: reveal_type(f(x)) # N: Revealed type is "T`-1" reveal_type(f(1)) # N: Revealed type is "builtins.int" @@ -1036,7 +1109,6 @@ def f[T](x: T) -> T: return x [case testPEP695NonLocalAndGlobal] -# mypy: enable-incomplete-feature=NewGenericSyntax def f() -> None: T = 1 def g[T](x: T) -> T: @@ -1066,7 +1138,6 @@ class C[T]: return a [case testPEP695ArgumentDefault] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import cast def f[T]( @@ -1086,7 +1157,6 @@ class C: [typing fixtures/typing-full.pyi] [case testPEP695ListComprehension] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import cast def f[T](x: T) -> T: @@ -1095,8 +1165,6 @@ def f[T](x: T) -> T: return x [case testPEP695ReuseNameInSameScope] -# mypy: enable-incomplete-feature=NewGenericSyntax - class C[T]: def m[S](self, x: S, y: T) -> S | T: return x @@ -1118,7 +1186,6 @@ def g[T](x: T) -> T: return x [case testPEP695NestedScopingSpecialCases] -# mypy: enable-incomplete-feature=NewGenericSyntax # This is adapted from PEP 695 S = 0 @@ -1135,7 +1202,6 @@ def outer1[S]() -> None: global S [case testPEP695ScopingWithBaseClasses] -# mypy: enable-incomplete-feature=NewGenericSyntax # This is adapted from PEP 695 class Outer: class Private: @@ -1151,7 +1217,6 @@ class Outer: return a [case testPEP695RedefineTypeParameterInScope] -# mypy: enable-incomplete-feature=NewGenericSyntax class C[T]: def m[T](self, x: T) -> T: # E: "T" already defined as a type parameter return x @@ -1163,7 +1228,6 @@ def f[S, S](x: S) -> S: # E: "S" already defined as a type parameter return x [case testPEP695ClassDecorator] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import Any T = 0 @@ -1175,8 +1239,6 @@ class C[T]: pass [case testPEP695RecursiceTypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax - type A = str | list[A] a: A reveal_type(a) # N: Revealed type is "Union[builtins.str, builtins.list[...]]" @@ -1189,8 +1251,6 @@ reveal_type(b) # N: Revealed type is "Union[__main__.C[builtins.int], builtins. [builtins fixtures/type.pyi] [case testPEP695BadRecursiveTypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax - type A = A # E: Cannot resolve name "A" (possible cyclic definition) type B = B | int # E: Invalid recursive alias: a union item of itself a: A @@ -1201,8 +1261,6 @@ reveal_type(b) # N: Revealed type is "Any" [typing fixtures/typing-full.pyi] [case testPEP695RecursiveTypeAliasForwardReference] -# mypy: enable-incomplete-feature=NewGenericSyntax - def f(a: A) -> None: if isinstance(a, str): reveal_type(a) # N: Revealed type is "builtins.str" @@ -1221,7 +1279,6 @@ f(C[int]()) # E: Argument 1 to "f" has incompatible type "C[int]"; expected "A" [builtins fixtures/isinstance.pyi] [case testPEP695InvalidGenericOrProtocolBaseClass] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import Generic, Protocol, TypeVar S = TypeVar("S") @@ -1239,8 +1296,14 @@ class P[T](Protocol[T]): # E: No arguments expected for "Protocol" base class class P2[T](Protocol[S]): # E: No arguments expected for "Protocol" base class pass +[case testPEP695CannotUseTypeVarFromOuterClass] +class ClassG[V]: + # This used to crash + class ClassD[T: dict[str, V]]: # E: Name "V" is not defined + ... +[builtins fixtures/dict.pyi] + [case testPEP695MixNewAndOldStyleGenerics] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import TypeVar S = TypeVar("S") @@ -1261,7 +1324,6 @@ class D[T](C[S]): # E: All type parameters should be declared ("S" not declared pass [case testPEP695MixNewAndOldStyleTypeVarTupleAndParamSpec] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import TypeVarTuple, ParamSpec, Callable Ts = TypeVarTuple("Ts") P = ParamSpec("P") @@ -1273,7 +1335,6 @@ def g[T](x: T, f: tuple[*Ts] # E: All type parameters should be declared ("Ts" [builtins fixtures/tuple.pyi] [case testPEP695MixNewAndOldStyleGenericsInTypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import TypeVar, ParamSpec, TypeVarTuple, Callable T = TypeVar("T") @@ -1290,7 +1351,6 @@ type C = Callable[P, None] # E: All type parameters should be declared ("P" not [typing fixtures/typing-full.pyi] [case testPEP695NonGenericAliasToGenericClass] -# mypy: enable-incomplete-feature=NewGenericSyntax class C[T]: pass type A = C x: C @@ -1300,7 +1360,6 @@ reveal_type(y) # N: Revealed type is "__main__.C[Any]" z: A[int] # E: Bad number of arguments for type alias, expected 0, given 1 [case testPEP695SelfType] -# mypy: enable-incomplete-feature=NewGenericSyntax from typing import Self class C: @@ -1331,8 +1390,6 @@ reveal_type(F[str]().mm(b'x')) # N: Revealed type is "Tuple[__main__.F[builtins [builtins fixtures/tuple.pyi] [case testPEP695CallAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax - class C: def __init__(self, x: str) -> None: ... type A = C @@ -1355,7 +1412,6 @@ B2[int]() [typing fixtures/typing-full.pyi] [case testPEP695IncrementalTypeAliasKinds] -# flags: --enable-incomplete-feature=NewGenericSyntax import a [file a.py] @@ -1378,7 +1434,6 @@ C: TypeAlias = int tmp/a.py:2: error: "TypeAliasType" not callable [case testPEP695TypeAliasBoundAndValueChecking] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Any, cast class C: pass @@ -1419,8 +1474,6 @@ c4: A3[int, str] # E: Type argument "int" of "A3" must be a subtype of "C" [typing fixtures/typing-full.pyi] [case testPEP695TypeAliasInClassBodyOrFunction] -# flags: --enable-incomplete-feature=NewGenericSyntax - class C: type A = int type B[T] = list[T] | None @@ -1472,17 +1525,38 @@ reveal_type(E[str]().a) # N: Revealed type is "builtins.list[Any]" [builtins fixtures/type.pyi] [typing fixtures/typing-full.pyi] +[case testPEP695TypeAliasInvalidGenericConstraint] +class A[T]: + class a[S: (int, list[T])]: pass # E: Name "T" is not defined + type b[S: (int, list[T])] = S # E: TypeVar constraint type cannot be parametrized by type variables + def c[S: (int, list[T])](self) -> None: ... # E: TypeVar constraint type cannot be parametrized by type variables +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeAliasUnboundTypeVarConstraint] +from typing import TypeVar +T = TypeVar("T") +class a[S: (int, list[T])]: pass # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) +type b[S: (int, list[T])] = S # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) +def c[S: (int, list[T])](self) -> None: ... # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + [case testPEP695RedefineAsTypeAlias1] -# flags: --enable-incomplete-feature=NewGenericSyntax class C: pass -type C = int # E: Name "C" already defined on line 2 +type C = int # E: Name "C" already defined on line 1 A = 0 -type A = str # E: Name "A" already defined on line 5 +type A = str # E: Name "A" already defined on line 4 reveal_type(A) # N: Revealed type is "builtins.int" [case testPEP695RedefineAsTypeAlias2] -# flags: --enable-incomplete-feature=NewGenericSyntax from m import D type D = int # E: Name "D" already defined (possibly by an import) a: D @@ -1491,30 +1565,26 @@ reveal_type(a) # N: Revealed type is "m.D" class D: pass [case testPEP695RedefineAsTypeAlias3] -# flags: --enable-incomplete-feature=NewGenericSyntax D = list["Forward"] -type D = int # E: Name "D" already defined on line 2 +type D = int # E: Name "D" already defined on line 1 Forward = str x: D reveal_type(x) # N: Revealed type is "builtins.list[builtins.str]" [case testPEP695MultiDefinitionsForTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax if int(): type A[T] = list[T] else: - type A[T] = str # E: Name "A" already defined on line 3 + type A[T] = str # E: Name "A" already defined on line 2 x: T # E: Name "T" is not defined a: A[int] reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" [case testPEP695UndefinedNameInAnnotation] -# flags: --enable-incomplete-feature=NewGenericSyntax def f[T](x: foobar, y: T) -> T: ... # E: Name "foobar" is not defined reveal_type(f) # N: Revealed type is "def [T] (x: Any, y: T`-1) -> T`-1" [case testPEP695WrongNumberOfConstrainedTypes] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T: ()] = list[T] # E: Type variable must have at least two constrained types a: A[int] reveal_type(a) # N: Revealed type is "builtins.list[builtins.int]" @@ -1524,7 +1594,6 @@ b: B[str] reveal_type(b) # N: Revealed type is "builtins.list[builtins.str]" [case testPEP695UsingTypeVariableInOwnBoundOrConstraint] -# flags: --enable-incomplete-feature=NewGenericSyntax type A[T: list[T]] = str # E: Name "T" is not defined type B[S: (list[S], str)] = str # E: Name "S" is not defined type C[T, S: list[T]] = str # E: Name "T" is not defined @@ -1534,7 +1603,6 @@ class D[T: T]: # E: Name "T" is not defined pass [case testPEP695InvalidType] -# flags: --enable-incomplete-feature=NewGenericSyntax def f[T: 1](x: T) -> T: ... # E: Invalid type: try using Literal[1] instead? class C[T: (int, (1 + 2))]: pass # E: Invalid type comment or annotation type A = list[1] # E: Invalid type: try using Literal[1] instead? @@ -1545,7 +1613,6 @@ b: B reveal_type(b) # N: Revealed type is "Any" [case testPEP695GenericNamedTuple] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import NamedTuple # Invariant because of the signature of the generated _replace method @@ -1572,7 +1639,6 @@ e: M[bool] # E: Value of type variable "T" of "M" cannot be "bool" [builtins fixtures/tuple.pyi] [case testPEP695GenericTypedDict] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import TypedDict class D[T](TypedDict): @@ -1593,7 +1659,6 @@ d: E[int] # E: Type argument "int" of "E" must be a subtype of "str" [typing fixtures/typing-full.pyi] [case testCurrentClassWorksAsBound] -# flags: --enable-incomplete-feature=NewGenericSyntax from typing import Protocol class Comparable[T: Comparable](Protocol): @@ -1606,7 +1671,6 @@ x: Comparable[Good] y: Comparable[int] # E: Type argument "int" of "Comparable" must be a subtype of "Comparable[Any]" [case testPEP695TypeAliasWithDifferentTargetTypes] -# flags: --enable-incomplete-feature=NewGenericSyntax import types # We need GenericAlias from here, and test stubs don't bring in 'types' from typing import Any, Callable, List, Literal, TypedDict @@ -1656,3 +1720,255 @@ type I2 = C[Any] | None type I3 = None | C[TD] [builtins fixtures/type.pyi] [typing fixtures/typing-full.pyi] + +[case testTypedDictInlineYesNewStyleAlias] +# flags: --enable-incomplete-feature=InlineTypedDict +type X[T] = {"item": T, "other": X[T] | None} +x: X[str] +reveal_type(x) # N: Revealed type is "TypedDict({'item': builtins.str, 'other': Union[..., None]})" +if x["other"] is not None: + reveal_type(x["other"]["item"]) # N: Revealed type is "builtins.str" + +type Y[T] = {"item": T, **Y[T]} # E: Overwriting TypedDict field "item" while merging +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695UsingIncorrectExpressionsInTypeVariableBound] +type X[T: (yield 1)] = Any # E: Yield expression cannot be used as a type variable bound +type Y[T: (yield from [])] = Any # E: Yield expression cannot be used as a type variable bound +type Z[T: (a := 1)] = Any # E: Named expression cannot be used as a type variable bound +type K[T: (await 1)] = Any # E: Await expression cannot be used as a type variable bound + +type XNested[T: (1 + (yield 1))] = Any # E: Yield expression cannot be used as a type variable bound +type YNested[T: (1 + (yield from []))] = Any # E: Yield expression cannot be used as a type variable bound +type ZNested[T: (1 + (a := 1))] = Any # E: Named expression cannot be used as a type variable bound +type KNested[T: (1 + (await 1))] = Any # E: Await expression cannot be used as a type variable bound + +class FooX[T: (yield 1)]: pass # E: Yield expression cannot be used as a type variable bound +class FooY[T: (yield from [])]: pass # E: Yield expression cannot be used as a type variable bound +class FooZ[T: (a := 1)]: pass # E: Named expression cannot be used as a type variable bound +class FooK[T: (await 1)]: pass # E: Await expression cannot be used as a type variable bound + +class FooXNested[T: (1 + (yield 1))]: pass # E: Yield expression cannot be used as a type variable bound +class FooYNested[T: (1 + (yield from []))]: pass # E: Yield expression cannot be used as a type variable bound +class FooZNested[T: (1 + (a := 1))]: pass # E: Named expression cannot be used as a type variable bound +class FooKNested[T: (1 + (await 1))]: pass # E: Await expression cannot be used as a type variable bound + +def foox[T: (yield 1)](): pass # E: Yield expression cannot be used as a type variable bound +def fooy[T: (yield from [])](): pass # E: Yield expression cannot be used as a type variable bound +def fooz[T: (a := 1)](): pass # E: Named expression cannot be used as a type variable bound +def fook[T: (await 1)](): pass # E: Await expression cannot be used as a type variable bound + +def foox_nested[T: (1 + (yield 1))](): pass # E: Yield expression cannot be used as a type variable bound +def fooy_nested[T: (1 + (yield from []))](): pass # E: Yield expression cannot be used as a type variable bound +def fooz_nested[T: (1 + (a := 1))](): pass # E: Named expression cannot be used as a type variable bound +def fook_nested[T: (1 +(await 1))](): pass # E: Await expression cannot be used as a type variable bound + +[case testPEP695UsingIncorrectExpressionsInTypeAlias] +type X = (yield 1) # E: Yield expression cannot be used within a type alias +type Y = (yield from []) # E: Yield expression cannot be used within a type alias +type Z = (a := 1) # E: Named expression cannot be used within a type alias +type K = (await 1) # E: Await expression cannot be used within a type alias + +type XNested = (1 + (yield 1)) # E: Yield expression cannot be used within a type alias +type YNested = (1 + (yield from [])) # E: Yield expression cannot be used within a type alias +type ZNested = (1 + (a := 1)) # E: Named expression cannot be used within a type alias +type KNested = (1 + (await 1)) # E: Await expression cannot be used within a type alias + +[case testPEP695TypeAliasAndAnnotated] +from typing_extensions import Annotated, Annotated as _Annotated +import typing_extensions as t + +def ann(*args): ... + +type A = Annotated[int, ann()] +type B = Annotated[int | str, ann((1, 2))] +type C = _Annotated[int, ann()] +type D = t.Annotated[str, ann()] + +x: A +y: B +z: C +zz: D +reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(y) # N: Revealed type is "Union[builtins.int, builtins.str]" +reveal_type(z) # N: Revealed type is "builtins.int" +reveal_type(zz) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + +[case testPEP695NestedGenericClass1] +class C[T]: + def f(self) -> T: ... + +class A: + class B[Q]: + def __init__(self, a: Q) -> None: + self.a = a + + def f(self) -> Q: + return self.a + + def g(self, x: Q) -> None: ... + + b: B[str] + +x: A.B[int] +x.g("x") # E: Argument 1 to "g" of "B" has incompatible type "str"; expected "int" +reveal_type(x.a) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "__main__.A.B[builtins.int]" +reveal_type(A.b) # N: Revealed type is "__main__.A.B[builtins.str]" + +[case testPEP695NestedGenericClass2] +class A: + def m(self) -> None: + class B[T]: + def f(self) -> T: ... + x: B[int] + reveal_type(x.f()) # N: Revealed type is "builtins.int" + self.a = B[str]() + +reveal_type(A().a) # N: Revealed type is "__main__.B@3[builtins.str]" +reveal_type(A().a.f()) # N: Revealed type is "builtins.str" + +[case testPEP695NestedGenericClass3] +class C[T]: + def f(self) -> T: ... + class D[S]: + x: T # E: Name "T" is not defined + def g(self) -> S: ... + +a: C[int] +reveal_type(a.f()) # N: Revealed type is "builtins.int" +b: C.D[str] +reveal_type(b.g()) # N: Revealed type is "builtins.str" + +class E[T]: + class F[T]: # E: "T" already defined as a type parameter + x: T + +c: E.F[int] + +[case testPEP695NestedGenericClass4] +class A: + class B[T]: + def __get__(self, instance: A, owner: type[A]) -> T: + return None # E: Incompatible return value type (got "None", expected "T") + f = B[int]() + +a = A() +v = a.f + +[case testPEP695VarianceInheritedFromBaseWithExplicitVariance] +from typing import TypeVar, Generic + +T = TypeVar("T") + +class ParentInvariant(Generic[T]): + pass + +class Invariant1[T](ParentInvariant[T]): + pass + +a1: Invariant1[int] = Invariant1[float]() # E: Incompatible types in assignment (expression has type "Invariant1[float]", variable has type "Invariant1[int]") +a2: Invariant1[float] = Invariant1[int]() # E: Incompatible types in assignment (expression has type "Invariant1[int]", variable has type "Invariant1[float]") + +T_contra = TypeVar("T_contra", contravariant=True) + +class ParentContravariant(Generic[T_contra]): + pass + +class Contravariant[T](ParentContravariant[T]): + pass + +b1: Contravariant[int] = Contravariant[float]() +b2: Contravariant[float] = Contravariant[int]() # E: Incompatible types in assignment (expression has type "Contravariant[int]", variable has type "Contravariant[float]") + +class Invariant2[T](ParentContravariant[T]): + def f(self) -> T: ... + +c1: Invariant2[int] = Invariant2[float]() # E: Incompatible types in assignment (expression has type "Invariant2[float]", variable has type "Invariant2[int]") +c2: Invariant2[float] = Invariant2[int]() # E: Incompatible types in assignment (expression has type "Invariant2[int]", variable has type "Invariant2[float]") + +class Multi[T, S](ParentInvariant[T], ParentContravariant[S]): + pass + +d1: Multi[int, str] = Multi[float, str]() # E: Incompatible types in assignment (expression has type "Multi[float, str]", variable has type "Multi[int, str]") +d2: Multi[float, str] = Multi[int, str]() # E: Incompatible types in assignment (expression has type "Multi[int, str]", variable has type "Multi[float, str]") +d3: Multi[str, int] = Multi[str, float]() +d4: Multi[str, float] = Multi[str, int]() # E: Incompatible types in assignment (expression has type "Multi[str, int]", variable has type "Multi[str, float]") + +[case testPEP695MultipleNestedGenericClass1] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + class B: + class C: + class D[Q]: + def g(self, x: Q): ... + d: D[str] + +x: A.B.C.D[int] +x.g('a') # E: Argument 1 to "g" of "D" has incompatible type "str"; expected "int" +reveal_type(x) # N: Revealed type is "__main__.A.B.C.D[builtins.int]" +reveal_type(A.B.C.d) # N: Revealed type is "__main__.A.B.C.D[builtins.str]" + +[case testPEP695MultipleNestedGenericClass2] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + class B: + def m(self) -> None: + class C[T]: + def f(self) -> T: ... + x: C[int] + reveal_type(x.f()) # N: Revealed type is "builtins.int" + self.a = C[str]() + +reveal_type(A().B().a) # N: Revealed type is "__main__.C@5[builtins.str]" + +[case testPEP695MultipleNestedGenericClass3] +# flags: --enable-incomplete-feature=NewGenericSyntax +class A: + class C[T]: + def f(self) -> T: ... + class D[S]: + x: T # E: Name "T" is not defined + def g(self) -> S: ... + +a: A.C[int] +reveal_type(a.f()) # N: Revealed type is "builtins.int" +b: A.C.D[str] +reveal_type(b.g()) # N: Revealed type is "builtins.str" + +class B: + class E[T]: + class F[T]: # E: "T" already defined as a type parameter + x: T + +c: B.E.F[int] + +[case testPEP695MultipleNestedGenericClass4] +# flags: --enable-incomplete-feature=NewGenericSyntax +class Z: + class A: + class B[T]: + def __get__(self, instance: Z.A, owner: type[Z.A]) -> T: + return None # E: Incompatible return value type (got "None", expected "T") + f = B[int]() + +a = Z.A() +v = a.f + +[case testPEP695MultipleNestedGenericClass5] +# flags: --enable-incomplete-feature=NewGenericSyntax +from a.b.c import d +x: d.D.E.F.G[int] +x.g('a') # E: Argument 1 to "g" of "G" has incompatible type "str"; expected "int" +reveal_type(x) # N: Revealed type is "a.b.c.d.D.E.F.G[builtins.int]" +reveal_type(d.D.E.F.d) # N: Revealed type is "a.b.c.d.D.E.F.G[builtins.str]" + +[file a/b/c/d.py] +class D: + class E: + class F: + class G[Q]: + def g(self, x: Q): ... + d: G[str] diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test new file mode 100644 index 0000000000000..2729ad3e21d1a --- /dev/null +++ b/test-data/unit/check-python313.test @@ -0,0 +1,257 @@ +[case testPEP695TypeParameterDefaultSupported] +class C[T = None]: ... +def f[T = list[int]]() -> None: ... +def g[**P = [int, str]]() -> None: ... +type A[T, S = int, U = str] = list[T] + +[case testPEP695TypeParameterDefaultBasic] +from typing import Callable + +def f1[T1 = int](a: T1) -> list[T1]: ... +reveal_type(f1) # N: Revealed type is "def [T1 = builtins.int] (a: T1`-1 = builtins.int) -> builtins.list[T1`-1 = builtins.int]" + +def f2[**P1 = [int, str]](a: Callable[P1, None]) -> Callable[P1, None]: ... +reveal_type(f2) # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] (a: def (*P1.args, **P1.kwargs)) -> def (*P1.args, **P1.kwargs)" + +def f3[*Ts1 = *tuple[int, str]](a: tuple[*Ts1]) -> tuple[*Ts1]: ... +reveal_type(f3) # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] (a: Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]) -> Tuple[Unpack[Ts1`-1 = Unpack[Tuple[builtins.int, builtins.str]]]]" + + +class ClassA1[T1 = int]: ... +class ClassA2[**P1 = [int, str]]: ... +class ClassA3[*Ts1 = *tuple[int, str]]: ... + +reveal_type(ClassA1) # N: Revealed type is "def [T1 = builtins.int] () -> __main__.ClassA1[T1`1 = builtins.int]" +reveal_type(ClassA2) # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] () -> __main__.ClassA2[P1`1 = [builtins.int, builtins.str]]" +reveal_type(ClassA3) # N: Revealed type is "def [Ts1 = Unpack[Tuple[builtins.int, builtins.str]]] () -> __main__.ClassA3[Unpack[Ts1`1 = Unpack[Tuple[builtins.int, builtins.str]]]]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultValid] +from typing import Any + +class ClassT1[T = int]: ... +class ClassT2[T: float = int]: ... +class ClassT3[T: list[Any] = list[int]]: ... +class ClassT4[T: (int, str) = int]: ... + +class ClassP1[**P = []]: ... +class ClassP2[**P = ...]: ... +class ClassP3[**P = [int, str]]: ... + +class ClassTs1[*Ts = *tuple[int]]: ... +class ClassTs2[*Ts = *tuple[int, ...]]: ... +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultInvalid] +class ClassT1[T = 2]: ... # E: TypeVar "default" must be a type +class ClassT2[T = [int]]: ... # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: TypeVar "default" must be a type +class ClassT3[T: str = int]: ... # E: TypeVar default must be a subtype of the bound type +class ClassT4[T: list[str] = list[int]]: ... # E: TypeVar default must be a subtype of the bound type +class ClassT5[T: (int, str) = bytes]: ... # E: TypeVar default must be one of the constraint types +class ClassT6[T: (int, str) = int | str]: ... # E: TypeVar default must be one of the constraint types +class ClassT7[T: (float, str) = int]: ... # E: TypeVar default must be one of the constraint types + +class ClassP1[**P = int]: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +class ClassP2[**P = 2]: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +class ClassP3[**P = (2, int)]: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +class ClassP4[**P = [2, int]]: ... # E: Argument 0 of ParamSpec default must be a type + +class ClassTs1[*Ts = 2]: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +class ClassTs2[*Ts = int]: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +class ClassTs3[*Ts = tuple[int]]: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultInvalid2] +from typing import overload +def f1[T = 2]() -> None: ... # E: TypeVar "default" must be a type +def f2[T = [int]]() -> None: ... # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: TypeVar "default" must be a type +def f3[T: str = int](x: T) -> T: ... # E: TypeVar default must be a subtype of the bound type +def f4[T: list[str] = list[int]](x: T) -> T: ... # E: TypeVar default must be a subtype of the bound type +def f5[T: (int, str) = bytes](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f6[T: (int, str) = int | str](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f7[T: (float, str) = int](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f8[T: str = int]() -> None: ... # TODO check unused TypeVars +@overload +def f9[T: str = int](x: T) -> T: ... # E: TypeVar default must be a subtype of the bound type +@overload +def f9[T: (int, str) = bytes](x: T) -> T: ... # E: TypeVar default must be one of the constraint types +def f9() -> None: ... # type: ignore[misc] + +def g1[**P = int]() -> None: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +def g2[**P = 2]() -> None: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +def g3[**P = (2, int)]() -> None: ... # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +def g4[**P = [2, int]]() -> None: ... # E: Argument 0 of ParamSpec default must be a type + +def h1[*Ts = 2]() -> None: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +def h2[*Ts = int]() -> None: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +def h3[*Ts = tuple[int]]() -> None: ... # E: The default argument to TypeVarTuple must be an Unpacked tuple +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultInvalid3] +from typing import Callable + +type TA1[T: str = 1] = list[T] # E: TypeVar "default" must be a type +type TA2[T: str = [int]] = list[T] # E: Bracketed expression "[...]" is not valid as a type \ + # N: Did you mean "List[...]"? \ + # E: TypeVar "default" must be a type +type TA3[T: str = int] = list[T] # E: TypeVar default must be a subtype of the bound type +type TA4[T: list[str] = list[int]] = list[T] # E: TypeVar default must be a subtype of the bound type +type TA5[T: (int, str) = bytes] = list[T] # E: TypeVar default must be one of the constraint types +type TA6[T: (int, str) = int | str] = list[T] # E: TypeVar default must be one of the constraint types +type TA7[T: (float, str) = int] = list[T] # E: TypeVar default must be one of the constraint types + +type TB1[**P = int] = Callable[P, None] # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +type TB2[**P = 2] = Callable[P, None] # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +type TB3[**P = (2, int)] = Callable[P, None] # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec +type TB4[**P = [2, int]] = Callable[P, None] # E: Argument 0 of ParamSpec default must be a type + +type TC1[*Ts = 2] = tuple[*Ts] # E: The default argument to TypeVarTuple must be an Unpacked tuple +type TC2[*Ts = int] = tuple[*Ts] # E: The default argument to TypeVarTuple must be an Unpacked tuple +type TC3[*Ts = tuple[int]] = tuple[*Ts] # E: The default argument to TypeVarTuple must be an Unpacked tuple +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeParameterDefaultFunctions] +from typing import Callable + +def callback1(x: str) -> None: ... + +def func_a1[T = str](x: int | T) -> T: ... +reveal_type(func_a1(2)) # N: Revealed type is "builtins.str" +reveal_type(func_a1(2.1)) # N: Revealed type is "builtins.float" + +def func_a2[T = str](x: int | T) -> list[T]: ... +reveal_type(func_a2(2)) # N: Revealed type is "builtins.list[builtins.str]" +reveal_type(func_a2(2.1)) # N: Revealed type is "builtins.list[builtins.float]" + + +def func_a3[T: str = str](x: int | T) -> T: ... +reveal_type(func_a3(2)) # N: Revealed type is "builtins.str" + +def func_a4[T: (bytes, str) = str](x: int | T) -> T: ... +reveal_type(func_a4(2)) # N: Revealed type is "builtins.str" + +def func_b1[**P = [int, str]](x: int | Callable[P, None]) -> Callable[P, None]: ... +reveal_type(func_b1(callback1)) # N: Revealed type is "def (x: builtins.str)" +reveal_type(func_b1(2)) # N: Revealed type is "def (builtins.int, builtins.str)" + +def func_c1[*Ts = *tuple[int, str]](x: int | Callable[[*Ts], None]) -> tuple[*Ts]: ... +# reveal_type(func_c1(callback1)) # Revealed type is "Tuple[str]" # TODO +reveal_type(func_c1(2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultClass1] +# flags: --disallow-any-generics + +class ClassA1[T2 = int, T3 = str]: ... + +def func_a1( + a: ClassA1, + b: ClassA1[float], + c: ClassA1[float, float], + d: ClassA1[float, float, float], # E: "ClassA1" expects between 0 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.str]" + reveal_type(c) # N: Revealed type is "__main__.ClassA1[builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "__main__.ClassA1[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultClass2] +# flags: --disallow-any-generics + +class ClassB1[**P2 = [int, str], **P3 = ...]: ... + +def func_b1( + a: ClassB1, + b: ClassB1[[float]], + c: ClassB1[[float], [float]], + d: ClassB1[[float], [float], [float]], # E: "ClassB1" expects between 0 and 2 type arguments, but 3 given +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]" + reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], ...]" + reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], ...]" + + k = ClassB1() + reveal_type(k) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" + l = ClassB1[[float]]() + reveal_type(l) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]" + m = ClassB1[[float], [float]]() + reveal_type(m) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + n = ClassB1[[float], [float], [float]]() # E: Type application has too many types (expected between 0 and 2) + reveal_type(n) # N: Revealed type is "Any" + +[case testPEP695TypeParameterDefaultClass3] +# flags: --disallow-any-generics + +class ClassC1[*Ts = *tuple[int, str]]: ... + +def func_c1( + a: ClassC1, + b: ClassC1[float], +) -> None: + # reveal_type(a) # Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "__main__.ClassC1[builtins.float]" + + k = ClassC1() + reveal_type(k) # N: Revealed type is "__main__.ClassC1[builtins.int, builtins.str]" + l = ClassC1[float]() + reveal_type(l) # N: Revealed type is "__main__.ClassC1[builtins.float]" +[builtins fixtures/tuple.pyi] + +[case testPEP695TypeParameterDefaultTypeAlias1] +# flags: --disallow-any-generics + +type TA1[T2 = int, T3 = str] = dict[T2, T3] + +def func_a1( + a: TA1, + b: TA1[float], + c: TA1[float, float], + d: TA1[float, float, float], # E: Bad number of arguments for type alias, expected between 0 and 2, given 3 +) -> None: + reveal_type(a) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.float, builtins.str]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.float, builtins.float]" + reveal_type(d) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeParameterDefaultTypeAlias2] +# flags: --disallow-any-generics + +class ClassB1[**P2, **P3]: ... +type TB1[**P2 = [int, str], **P3 = ...] = ClassB1[P2, P3] + +def func_b1( + a: TB1, + b: TB1[[float]], + c: TB1[[float], [float]], + d: TB1[[float], [float], [float]], # E: Bad number of arguments for type alias, expected between 0 and 2, given 3 +) -> None: + reveal_type(a) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" + reveal_type(b) # N: Revealed type is "__main__.ClassB1[[builtins.float], [*Any, **Any]]" + reveal_type(c) # N: Revealed type is "__main__.ClassB1[[builtins.float], [builtins.float]]" + reveal_type(d) # N: Revealed type is "__main__.ClassB1[[builtins.int, builtins.str], [*Any, **Any]]" +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + +[case testPEP695TypeParameterDefaultTypeAlias3] +# flags: --disallow-any-generics + +type TC1[*Ts = *tuple[int, str]] = tuple[*Ts] + +def func_c1( + a: TC1, + b: TC1[float], +) -> None: + # reveal_type(a) # Revealed type is "Tuple[builtins.int, builtins.str]" # TODO + reveal_type(b) # N: Revealed type is "Tuple[builtins.float]" + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index dfb918defb0a5..199014a66fedb 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -463,9 +463,9 @@ def check_partial_list() -> None: if (x := 0): reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Literal[0]" -reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Literal[0]" [case testWalrusAssignmentAndConditionScopeForProperty] # flags: --warn-unreachable @@ -483,7 +483,7 @@ wrapper = PropertyWrapper() if x := wrapper.f: reveal_type(x) # N: Revealed type is "builtins.str" else: - reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Literal['']" reveal_type(x) # N: Revealed type is "builtins.str" @@ -505,7 +505,7 @@ def f() -> str: ... if x := f(): reveal_type(x) # N: Revealed type is "builtins.str" else: - reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Literal['']" reveal_type(x) # N: Revealed type is "builtins.str" diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index d5c8acd1bc15d..4d7af98204fb5 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -994,3 +994,23 @@ class T2(Tuple[T1, "T4", "T4"]): ... class T3(Tuple[str, "T4", "T4"]): ... T4 = Union[T2, T3] [builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback5] +from typing import Protocol, Tuple, Union + +class Proto(Protocol): + def __len__(self) -> int: ... + +A = Union[Proto, Tuple[A]] +ta: Tuple[A] +p: Proto +p = ta +[builtins fixtures/tuple.pyi] + +[case testRecursiveAliasesWithAnyUnimported] +# flags: --disallow-any-unimported +from typing import Callable +from bogus import Foo # type: ignore + +A = Callable[[Foo, "B"], Foo] # E: Type alias target becomes "Callable[[Any, B], Any]" due to an unfollowed import +B = Callable[[Foo, A], Foo] # E: Type alias target becomes "Callable[[Any, A], Any]" due to an unfollowed import diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 1480c83b22723..fa853ac48e5a8 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -2132,3 +2132,85 @@ class D: x: int x: Union[C, D] reveal_type(x.x) # N: Revealed type is "Union[__main__.C, builtins.int]" + +[case testCallableProtocolTypingSelf] +from typing import Protocol, Self + +class MyProtocol(Protocol): + __name__: str + + def __call__( + self: Self, + ) -> None: ... + +def test() -> None: ... +value: MyProtocol = test + +[case testCallableProtocolOldSelf] +from typing import Protocol, TypeVar + +Self = TypeVar("Self", bound="MyProtocol") + +class MyProtocol(Protocol): + __name__: str + + def __call__( + self: Self, + ) -> None: ... + +def test() -> None: ... +value: MyProtocol = test + +[case testSelfTypeUnionIter] +from typing import Self, Iterator, Generic, TypeVar, Union + +T = TypeVar("T") + +class range(Generic[T]): + def __iter__(self) -> Self: ... + def __next__(self) -> T: ... + +class count: + def __iter__(self) -> Iterator[int]: ... + +def foo(x: Union[range[int], count]) -> None: + for item in x: + reveal_type(item) # N: Revealed type is "builtins.int" + +[case testGenericDescriptorWithSelfTypeAnnotationsAndOverloads] +from __future__ import annotations +from typing import Any, overload, Callable, TypeVar, Generic, ParamSpec +from typing_extensions import Concatenate + +C = TypeVar("C", bound=Callable[..., Any]) +S = TypeVar("S") +P = ParamSpec("P") +R = TypeVar("R") + +class Descriptor(Generic[C]): + def __init__(self, impl: C) -> None: ... + + @overload + def __get__( + self: Descriptor[C], instance: None, owner: type | None + ) -> Descriptor[C]: ... + + @overload + def __get__( + self: Descriptor[Callable[Concatenate[S, P], R]], instance: S, owner: type | None, + ) -> Callable[P, R]: ... + + def __get__(self, *args, **kwargs): ... + +class Test: + @Descriptor + def method(self, foo: int, bar: str) -> bytes: ... + +reveal_type(Test().method) # N: Revealed type is "def (foo: builtins.int, bar: builtins.str) -> builtins.bytes" + +class Test2: + @Descriptor + def method(self, foo: int, *, bar: str) -> bytes: ... + +reveal_type(Test2().method) # N: Revealed type is "def (foo: builtins.int, *, bar: builtins.str) -> builtins.bytes" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index c6cf45d96691d..d7ab272aed6cf 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -151,4 +151,33 @@ class C: x: P[int] = C() [builtins fixtures/tuple.pyi] -[out] + +[case testSemanalDoesNotLeakSyntheticTypes] +# flags: --cache-fine-grained +from typing import Generic, NamedTuple, TypedDict, TypeVar +from dataclasses import dataclass + +T = TypeVar('T') +class Wrap(Generic[T]): pass + +invalid_1: 1 + 2 # E: Invalid type comment or annotation +invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +class A: + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +class B(NamedTuple): + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +class C(TypedDict): + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation + +@dataclass +class D: + invalid_1: 1 + 2 # E: Invalid type comment or annotation + invalid_2: Wrap[1 + 2] # E: Invalid type comment or annotation +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index d1464423e90f0..44880cf352043 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -519,6 +519,13 @@ if object(): raise BaseException from f # E: Exception must be derived from BaseException [builtins fixtures/exception.pyi] +[case testRaiseNotImplementedFails] +if object(): + raise NotImplemented # E: Exception must be derived from BaseException; did you mean "NotImplementedError"? +if object(): + raise NotImplemented() # E: NotImplemented? not callable +[builtins fixtures/notimplemented.pyi] + [case testTryFinallyStatement] import typing try: diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index bf36977b56e36..d675a35c4aae6 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1228,68 +1228,76 @@ x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "Tuple[ [out] [case testFixedTupleJoinVarTuple] -from typing import Tuple +from typing import Tuple, TypeVar class A: pass class B(A): pass fixtup: Tuple[B, B] +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + vartup_b: Tuple[B, ...] -reveal_type(fixtup if int() else vartup_b) # N: Revealed type is "builtins.tuple[__main__.B, ...]" -reveal_type(vartup_b if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.B, ...]" +reveal_type(join(fixtup, vartup_b)) # N: Revealed type is "builtins.tuple[__main__.B, ...]" +reveal_type(join(vartup_b, fixtup)) # N: Revealed type is "builtins.tuple[__main__.B, ...]" vartup_a: Tuple[A, ...] -reveal_type(fixtup if int() else vartup_a) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -reveal_type(vartup_a if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" - +reveal_type(join(fixtup, vartup_a)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(vartup_a, fixtup)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" [builtins fixtures/tuple.pyi] [out] [case testFixedTupleJoinList] -from typing import Tuple, List +from typing import Tuple, List, TypeVar class A: pass class B(A): pass fixtup: Tuple[B, B] +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + lst_b: List[B] -reveal_type(fixtup if int() else lst_b) # N: Revealed type is "typing.Sequence[__main__.B]" -reveal_type(lst_b if int() else fixtup) # N: Revealed type is "typing.Sequence[__main__.B]" +reveal_type(join(fixtup, lst_b)) # N: Revealed type is "typing.Sequence[__main__.B]" +reveal_type(join(lst_b, fixtup)) # N: Revealed type is "typing.Sequence[__main__.B]" lst_a: List[A] -reveal_type(fixtup if int() else lst_a) # N: Revealed type is "typing.Sequence[__main__.A]" -reveal_type(lst_a if int() else fixtup) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(fixtup, lst_a)) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(lst_a, fixtup)) # N: Revealed type is "typing.Sequence[__main__.A]" [builtins fixtures/tuple.pyi] [out] [case testEmptyTupleJoin] -from typing import Tuple, List +from typing import Tuple, List, TypeVar class A: pass empty = () +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + fixtup: Tuple[A] -reveal_type(fixtup if int() else empty) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -reveal_type(empty if int() else fixtup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(fixtup, empty)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(empty, fixtup)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" vartup: Tuple[A, ...] -reveal_type(empty if int() else vartup) # N: Revealed type is "builtins.tuple[__main__.A, ...]" -reveal_type(vartup if int() else empty) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(vartup, empty)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" +reveal_type(join(empty, vartup)) # N: Revealed type is "builtins.tuple[__main__.A, ...]" lst: List[A] -reveal_type(empty if int() else lst) # N: Revealed type is "typing.Sequence[__main__.A]" -reveal_type(lst if int() else empty) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(empty, lst)) # N: Revealed type is "typing.Sequence[__main__.A]" +reveal_type(join(lst, empty)) # N: Revealed type is "typing.Sequence[__main__.A]" [builtins fixtures/tuple.pyi] [out] [case testTupleSubclassJoin] -from typing import Tuple, NamedTuple +from typing import Tuple, NamedTuple, TypeVar class NTup(NamedTuple): a: bool @@ -1302,32 +1310,38 @@ ntup: NTup subtup: SubTuple vartup: SubVarTuple -reveal_type(ntup if int() else vartup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(subtup if int() else vartup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +reveal_type(join(ntup, vartup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(subtup, vartup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] [out] [case testTupleJoinIrregular] -from typing import Tuple +from typing import Tuple, TypeVar tup1: Tuple[bool, int] tup2: Tuple[bool] -reveal_type(tup1 if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup2 if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +reveal_type(join(tup1, tup2)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup2, tup1)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup1 if int() else ()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(() if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup1, ())) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join((), tup1)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup2 if int() else ()) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" -reveal_type(() if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +reveal_type(join(tup2, ())) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +reveal_type(join((), tup2)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" [builtins fixtures/tuple.pyi] [out] [case testTupleSubclassJoinIrregular] -from typing import Tuple, NamedTuple +from typing import Tuple, NamedTuple, TypeVar class NTup1(NamedTuple): a: bool @@ -1342,14 +1356,17 @@ tup1: NTup1 tup2: NTup2 subtup: SubTuple -reveal_type(tup1 if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" -reveal_type(tup2 if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +T = TypeVar("T") +def join(x: T, y: T) -> T: ... + +reveal_type(join(tup1, tup2)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" +reveal_type(join(tup2, tup1)) # N: Revealed type is "builtins.tuple[builtins.bool, ...]" -reveal_type(tup1 if int() else subtup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(subtup if int() else tup1) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup1, subtup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(subtup, tup1)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(tup2 if int() else subtup) # N: Revealed type is "builtins.tuple[builtins.int, ...]" -reveal_type(subtup if int() else tup2) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(tup2, subtup)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +reveal_type(join(subtup, tup2)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] [out] @@ -1421,6 +1438,12 @@ reveal_type(t[x:]) # N: Revealed type is "builtins.tuple[Union[builtins.int, bu t[y:] # E: Slice index must be an integer, SupportsIndex or None [builtins fixtures/tuple.pyi] +[case testTupleSliceStepZeroNoCrash] +# This was crashing: https://github.com/python/mypy/issues/18062 +# TODO: emit better error when 0 is used for step +()[::0] # E: Ambiguous slice of a variadic tuple +[builtins fixtures/tuple.pyi] + [case testInferTupleTypeFallbackAgainstInstance] from typing import TypeVar, Generic, Tuple T = TypeVar('T') diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 6f9e9eda1d024..c7b9694a91887 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1074,7 +1074,7 @@ x: TestType = 42 y: TestType = 'a' z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]") -reveal_type(TestType) # N: Revealed type is "typing.TypeAliasType" +reveal_type(TestType) # N: Revealed type is "typing_extensions.TypeAliasType" TestType() # E: "TypeAliasType" not callable class A: @@ -1084,6 +1084,15 @@ yc: A.ClassAlias = "" # E: Incompatible types in assignment (expression has typ [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] +[case testTypeAliasTypePython311] +# flags: --python-version 3.11 +# Pinning to 3.11, because 3.12 has `TypeAliasType` +from typing_extensions import TypeAliasType + +TestType = TypeAliasType("TestType", int) +x: TestType = 1 +[builtins fixtures/tuple.pyi] + [case testTypeAliasTypeInvalid] from typing_extensions import TypeAliasType diff --git a/test-data/unit/check-type-promotion.test b/test-data/unit/check-type-promotion.test index e66153726e7d1..d98d0c60e1644 100644 --- a/test-data/unit/check-type-promotion.test +++ b/test-data/unit/check-type-promotion.test @@ -91,7 +91,7 @@ else: reveal_type(x) # N: Revealed type is "builtins.complex" # Note we make type precise, since type promotions are involved -reveal_type(x) # N: Revealed type is "Union[builtins.complex, builtins.int, builtins.float]" +reveal_type(x) # N: Revealed type is "builtins.complex" [builtins fixtures/primitives.pyi] [case testIntersectionUsingPromotion3] @@ -127,7 +127,7 @@ if isinstance(x, int): reveal_type(x) # N: Revealed type is "builtins.int" else: reveal_type(x) # N: Revealed type is "Union[builtins.float, builtins.complex]" -reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.float, builtins.complex]" +reveal_type(x) # N: Revealed type is "Union[builtins.float, builtins.complex]" [builtins fixtures/primitives.pyi] [case testIntersectionUsingPromotion6] @@ -139,7 +139,7 @@ if isinstance(x, int): reveal_type(x) # N: Revealed type is "builtins.int" else: reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.complex]" -reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, builtins.complex]" +reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.complex]" [builtins fixtures/primitives.pyi] [case testIntersectionUsingPromotion7] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index fa77d98e4a34a..a30fec1b94220 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -78,7 +78,7 @@ p = Point(x='meaning_of_life', y=1337) # E: Incompatible types (expression has [case testCannotCreateTypedDictInstanceWithInlineTypedDict] from mypy_extensions import TypedDict D = TypedDict('D', { - 'x': TypedDict('E', { # E: Inline TypedDict types not supported; use assignment to define TypedDict + 'x': TypedDict('E', { # E: Use dict literal for nested TypedDict 'y': int }) }) @@ -2362,6 +2362,36 @@ Foo = TypedDict('Foo', {'camelCaseKey': str}) value: Foo = {} # E: Missing key "camelCaseKey" for TypedDict "Foo" [builtins fixtures/dict.pyi] +[case testTypedDictWithDeferredFieldTypeEval] +from typing import Generic, TypeVar, TypedDict, NotRequired + +class Foo(TypedDict): + y: NotRequired[int] + x: Outer[Inner[ForceDeferredEval]] + +var: Foo +reveal_type(var) # N: Revealed type is "TypedDict('__main__.Foo', {'y'?: builtins.int, 'x': __main__.Outer[__main__.Inner[__main__.ForceDeferredEval]]})" + +T1 = TypeVar("T1") +class Outer(Generic[T1]): pass + +T2 = TypeVar("T2", bound="ForceDeferredEval") +class Inner(Generic[T2]): pass + +class ForceDeferredEval: pass +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictRequiredUnimportedAny] +# flags: --disallow-any-unimported +from typing import NotRequired, TypedDict, ReadOnly +from nonexistent import Foo # type: ignore[import-not-found] +class Bar(TypedDict): + foo: NotRequired[Foo] # E: Type of variable becomes "Any" due to an unfollowed import + bar: ReadOnly[Foo] # E: Type of variable becomes "Any" due to an unfollowed import + baz: NotRequired[ReadOnly[Foo]] # E: Type of variable becomes "Any" due to an unfollowed import +[typing fixtures/typing-typeddict.pyi] + -- Required[] [case testDoesRecognizeRequiredInTypedDictWithClass] @@ -3550,3 +3580,476 @@ class Test: run(test2, other="yes", **params) run(test2, other=0, **params) # E: Argument "other" to "run" has incompatible type "int"; expected "str" [builtins fixtures/tuple.pyi] + +[case testTypedDictUnpackSingleWithSubtypingNoCrash] +from typing import Callable +from typing_extensions import TypedDict, Unpack + +class Kwargs(TypedDict): + name: str + +def f(**kwargs: Unpack[Kwargs]) -> None: + pass + +class C: + d: Callable[[Unpack[Kwargs]], None] + +# TODO: it is an old question whether we should allow this, for now simply don't crash. +class D(C): + d = f +[builtins fixtures/tuple.pyi] + +[case testTypedDictInlineNoOldStyleAlias] +# flags: --enable-incomplete-feature=InlineTypedDict +X = {"int": int, "str": str} +reveal_type(X) # N: Revealed type is "builtins.dict[builtins.str, def () -> builtins.object]" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineYesMidStyleAlias] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing_extensions import TypeAlias +X: TypeAlias = {"int": int, "str": str} +x: X +reveal_type(x) # N: # N: Revealed type is "TypedDict({'int': builtins.int, 'str': builtins.str})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineNoEmpty] +# flags: --enable-incomplete-feature=InlineTypedDict +x: {} # E: Invalid type comment or annotation +reveal_type(x) # N: Revealed type is "Any" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineNotRequired] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing import NotRequired + +x: {"one": int, "other": NotRequired[int]} +x = {"one": 1} # OK +y: {"one": int, "other": int} +y = {"one": 1} # E: Expected TypedDict keys ("one", "other") but found only key "one" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineReadOnly] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing import ReadOnly + +x: {"one": int, "other": ReadOnly[int]} +x["one"] = 1 # ok +x["other"] = 1 # E: ReadOnly TypedDict key "other" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineNestedSchema] +# flags: --enable-incomplete-feature=InlineTypedDict +def nested() -> {"one": str, "other": {"a": int, "b": int}}: + if bool(): + return {"one": "yes", "other": {"a": 1, "b": 2}} # OK + else: + return {"one": "no", "other": {"a": 1, "b": "2"}} # E: Incompatible types (expression has type "str", TypedDict item "b" has type "int") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictInlineMergeAnother] +# flags: --enable-incomplete-feature=InlineTypedDict +from typing import TypeVar +from typing_extensions import TypeAlias + +T = TypeVar("T") +X: TypeAlias = {"item": T} +x: {"a": int, **X[str], "b": int} +reveal_type(x) # N: Revealed type is "TypedDict({'a': builtins.int, 'b': builtins.int, 'item': builtins.str})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] + + +# ReadOnly +# See: https://peps.python.org/pep-0705 + +[case testTypedDictReadOnly] +# flags: --show-error-codes +from typing import ReadOnly, TypedDict + +class TP(TypedDict): + one: int + other: ReadOnly[str] + +x: TP +reveal_type(x["one"]) # N: Revealed type is "builtins.int" +reveal_type(x["other"]) # N: Revealed type is "builtins.str" +x["one"] = 1 # ok +x["other"] = "a" # E: ReadOnly TypedDict key "other" TypedDict is mutated [typeddict-readonly-mutated] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyCreation] +from typing import ReadOnly, TypedDict + +class TD(TypedDict): + x: ReadOnly[int] + y: int + +# Ok: +x = TD({"x": 1, "y": 2}) +y = TD(x=1, y=2) +z: TD = {"x": 1, "y": 2} + +# Error: +x2 = TD({"x": "a", "y": 2}) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +y2 = TD(x="a", y=2) # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +z2: TD = {"x": "a", "y": 2} # E: Incompatible types (expression has type "str", TypedDict item "x" has type "int") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyDel] +from typing import ReadOnly, TypedDict, NotRequired + +class TP(TypedDict): + required_key: ReadOnly[str] + optional_key: ReadOnly[NotRequired[str]] + +x: TP +del x["required_key"] # E: Key "required_key" of TypedDict "TP" cannot be deleted +del x["optional_key"] # E: Key "optional_key" of TypedDict "TP" cannot be deleted +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyMutateMethods] +from typing import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + other: ReadOnly[int] + mutable: bool + +x: TP +reveal_type(x.pop("key")) # E: Key "key" of TypedDict "TP" cannot be deleted \ + # N: Revealed type is "builtins.str" + +x.update({"key": "abc", "other": 1, "mutable": True}) # E: ReadOnly TypedDict keys ("key", "other") TypedDict are mutated +x.setdefault("key", "abc") # E: ReadOnly TypedDict key "key" TypedDict is mutated +x.setdefault("other", 1) # E: ReadOnly TypedDict key "other" TypedDict is mutated +x.setdefault("mutable", False) # ok +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFromTypingExtensionsReadOnlyMutateMethods] +from typing_extensions import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + +x: TP +x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictFromMypyExtensionsReadOnlyMutateMethods] +from mypy_extensions import TypedDict +from typing_extensions import ReadOnly + +class TP(TypedDict): + key: ReadOnly[str] + +x: TP +x.update({"key": "abc"}) # E: ReadOnly TypedDict key "key" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyMutate__ior__Statements] +from typing_extensions import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + other: ReadOnly[int] + mutable: bool + +x: TP +x |= {"mutable": True} # ok +x |= {"key": "a"} # E: ReadOnly TypedDict key "key" TypedDict is mutated +x |= {"key": "a", "other": 1, "mutable": True} # E: ReadOnly TypedDict keys ("key", "other") TypedDict are mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] + +[case testTypedDictReadOnlyMutate__or__Statements] +from typing_extensions import ReadOnly, TypedDict + +class TP(TypedDict): + key: ReadOnly[str] + other: ReadOnly[int] + mutable: bool + +x: TP +# These are new objects, not mutation: +x = x | {"mutable": True} +x = x | {"key": "a"} +x = x | {"key": "a", "other": 1, "mutable": True} +y1 = x | {"mutable": True} +y2 = x | {"key": "a"} +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict-iror.pyi] + +[case testTypedDictReadOnlyMutateWithOtherDicts] +from typing import ReadOnly, TypedDict, Dict + +class TP(TypedDict): + key: ReadOnly[str] + mutable: bool + +class Mutable(TypedDict): + mutable: bool + +class Regular(TypedDict): + key: str + +m: Mutable +r: Regular +d: Dict[str, object] + +# Creating new objects is ok: +tp: TP = {**r, **m} +tp1: TP = {**tp, **m} +tp2: TP = {**r, **m} +tp3: TP = {**tp, **r} +tp4: TP = {**tp, **d} # E: Unsupported type "Dict[str, object]" for ** expansion in TypedDict +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictGenericReadOnly] +from typing import ReadOnly, TypedDict, TypeVar, Generic + +T = TypeVar('T') + +class TP(TypedDict, Generic[T]): + key: ReadOnly[T] + +x: TP[int] +reveal_type(x["key"]) # N: Revealed type is "builtins.int" +x["key"] = 1 # E: ReadOnly TypedDict key "key" TypedDict is mutated +x["key"] = "a" # E: ReadOnly TypedDict key "key" TypedDict is mutated \ + # E: Value of "key" has incompatible type "str"; expected "int" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyOtherTypedDict] +from typing import ReadOnly, TypedDict + +class First(TypedDict): + field: int + +class TP(TypedDict): + key: ReadOnly[First] + +x: TP +reveal_type(x["key"]["field"]) # N: Revealed type is "builtins.int" +x["key"]["field"] = 1 # ok +x["key"] = {"field": 2} # E: ReadOnly TypedDict key "key" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyInheritance] +from typing import ReadOnly, TypedDict + +class Base(TypedDict): + a: ReadOnly[str] + +class Child(Base): + b: ReadOnly[int] + +base: Base +reveal_type(base["a"]) # N: Revealed type is "builtins.str" +base["a"] = "x" # E: ReadOnly TypedDict key "a" TypedDict is mutated +base["b"] # E: TypedDict "Base" has no key "b" + +child: Child +reveal_type(child["a"]) # N: Revealed type is "builtins.str" +reveal_type(child["b"]) # N: Revealed type is "builtins.int" +child["a"] = "x" # E: ReadOnly TypedDict key "a" TypedDict is mutated +child["b"] = 1 # E: ReadOnly TypedDict key "b" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlySubtyping] +from typing import ReadOnly, TypedDict + +class A(TypedDict): + key: ReadOnly[str] + +class B(TypedDict): + key: str + +a: A +b: B + +def accepts_A(d: A): ... +def accepts_B(d: B): ... + +accepts_A(a) +accepts_A(b) +accepts_B(a) # E: Argument 1 to "accepts_B" has incompatible type "A"; expected "B" +accepts_B(b) +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictRequiredConsistentWithNotRequiredReadOnly] +from typing import NotRequired, ReadOnly, Required, TypedDict + +class A(TypedDict): + x: NotRequired[ReadOnly[str]] + +class B(TypedDict): + x: Required[str] + +def f(b: B): + a: A = b # ok +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyCall] +from typing import ReadOnly, TypedDict + +TP = TypedDict("TP", {"one": int, "other": ReadOnly[str]}) + +x: TP +reveal_type(x["one"]) # N: Revealed type is "builtins.int" +reveal_type(x["other"]) # N: Revealed type is "builtins.str" +x["one"] = 1 # ok +x["other"] = "a" # E: ReadOnly TypedDict key "other" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyABCSubtypes] +from typing import ReadOnly, TypedDict, Mapping, Dict, MutableMapping + +class TP(TypedDict): + one: int + other: ReadOnly[int] + +def accepts_mapping(m: Mapping[str, object]): ... +def accepts_mutable_mapping(mm: MutableMapping[str, object]): ... +def accepts_dict(d: Dict[str, object]): ... + +x: TP +accepts_mapping(x) +accepts_mutable_mapping(x) # E: Argument 1 to "accepts_mutable_mapping" has incompatible type "TP"; expected "MutableMapping[str, object]" +accepts_dict(x) # E: Argument 1 to "accepts_dict" has incompatible type "TP"; expected "Dict[str, object]" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyAndNotRequired] +from typing import ReadOnly, TypedDict, NotRequired + +class TP(TypedDict): + one: ReadOnly[NotRequired[int]] + two: NotRequired[ReadOnly[str]] + +x: TP +reveal_type(x) # N: Revealed type is "TypedDict('__main__.TP', {'one'?=: builtins.int, 'two'?=: builtins.str})" +reveal_type(x.get("one")) # N: Revealed type is "Union[builtins.int, None]" +reveal_type(x.get("two")) # N: Revealed type is "Union[builtins.str, None]" +x["one"] = 1 # E: ReadOnly TypedDict key "one" TypedDict is mutated +x["two"] = "a" # E: ReadOnly TypedDict key "two" TypedDict is mutated +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testMeetOfTypedDictsWithReadOnly] +from typing import TypeVar, Callable, TypedDict, ReadOnly +XY = TypedDict('XY', {'x': ReadOnly[int], 'y': int}) +YZ = TypedDict('YZ', {'y': int, 'z': ReadOnly[int]}) +T = TypeVar('T') +def f(x: Callable[[T, T], None]) -> T: pass +def g(x: XY, y: YZ) -> None: pass +reveal_type(f(g)) # N: Revealed type is "TypedDict({'x'=: builtins.int, 'y': builtins.int, 'z'=: builtins.int})" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyUnpack] +from typing_extensions import TypedDict, Unpack, ReadOnly + +class TD(TypedDict): + x: ReadOnly[int] + y: str + +def func(**kwargs: Unpack[TD]): + kwargs["x"] = 1 # E: ReadOnly TypedDict key "x" TypedDict is mutated + kwargs["y" ] = "a" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testIncorrectTypedDictSpecialFormsUsage] +from typing import ReadOnly, TypedDict, NotRequired, Required + +x: ReadOnly[int] # E: ReadOnly[] can be only used in a TypedDict definition +y: Required[int] # E: Required[] can be only used in a TypedDict definition +z: NotRequired[int] # E: NotRequired[] can be only used in a TypedDict definition + +class TP(TypedDict): + a: ReadOnly[ReadOnly[int]] # E: "ReadOnly[]" type cannot be nested + b: ReadOnly[NotRequired[ReadOnly[str]]] # E: "ReadOnly[]" type cannot be nested + c: NotRequired[Required[int]] # E: "Required[]" type cannot be nested + d: Required[NotRequired[int]] # E: "NotRequired[]" type cannot be nested + e: Required[ReadOnly[NotRequired[int]]] # E: "NotRequired[]" type cannot be nested + f: ReadOnly[ReadOnly[ReadOnly[int]]] # E: "ReadOnly[]" type cannot be nested + g: Required[Required[int]] # E: "Required[]" type cannot be nested + h: NotRequired[NotRequired[int]] # E: "NotRequired[]" type cannot be nested + + j: NotRequired[ReadOnly[Required[ReadOnly[int]]]] # E: "Required[]" type cannot be nested \ + # E: "ReadOnly[]" type cannot be nested + + k: ReadOnly # E: "ReadOnly[]" must have exactly one type argument +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictAnnotatedWithSpecialForms] +from typing import NotRequired, ReadOnly, Required, TypedDict +from typing_extensions import Annotated + +class A(TypedDict): + a: Annotated[NotRequired[ReadOnly[int]], ""] # ok + b: NotRequired[ReadOnly[Annotated[int, ""]]] # ok +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + +[case testTypedDictReadOnlyCovariant] +from typing import ReadOnly, TypedDict, Union + +class A(TypedDict): + a: ReadOnly[Union[int, str]] + +class A2(TypedDict): + a: ReadOnly[int] + +class B(TypedDict): + a: int + +class B2(TypedDict): + a: Union[int, str] + +class B3(TypedDict): + a: int + +def fa(a: A) -> None: ... +def fa2(a: A2) -> None: ... + +b: B = {"a": 1} +fa(b) +fa2(b) +b2: B2 = {"a": 1} +fa(b2) +fa2(b2) # E: Argument 1 to "fa2" has incompatible type "B2"; expected "A2" + +class C(TypedDict): + a: ReadOnly[Union[int, str]] + b: Union[str, bytes] + +class D(TypedDict): + a: int + b: str + +d: D = {"a": 1, "b": "x"} +c: C = d # E: Incompatible types in assignment (expression has type "D", variable has type "C") +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index e1b7a86aba63e..e7a8eac4f043c 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -9,17 +9,6 @@ def main(a: object) -> None: reveal_type(a) # N: Revealed type is "builtins.object" [builtins fixtures/tuple.pyi] -[case testTypeGuardStringified] -from typing_extensions import TypeGuard -class Point: pass -def is_point(a: object) -> "TypeGuard[Point]": pass -def main(a: object) -> None: - if is_point(a): - reveal_type(a) # N: Revealed type is "__main__.Point" - else: - reveal_type(a) # N: Revealed type is "builtins.object" -[builtins fixtures/tuple.pyi] - [case testTypeGuardTypeArgsNone] from typing_extensions import TypeGuard def foo(a: object) -> TypeGuard: # E: TypeGuard must have exactly one type argument @@ -98,6 +87,15 @@ def main(a: Tuple[T, ...]): reveal_type(a) # N: Revealed type is "Tuple[T`-1, T`-1]" [builtins fixtures/tuple.pyi] +[case testTypeGuardPassedAsTypeVarIsBool] +from typing import Callable, TypeVar +from typing_extensions import TypeGuard +T = TypeVar('T') +def is_str(x: object) -> TypeGuard[str]: ... +def main(f: Callable[[object], T]) -> T: ... +reveal_type(main(is_str)) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + [case testTypeGuardNonOverlapping] from typing import List from typing_extensions import TypeGuard diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 83467d5e36830..2372f990fda1b 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -9,17 +9,6 @@ def main(a: object) -> None: reveal_type(a) # N: Revealed type is "builtins.object" [builtins fixtures/tuple.pyi] -[case testTypeIsStringified] -from typing_extensions import TypeIs -class Point: pass -def is_point(a: object) -> "TypeIs[Point]": pass -def main(a: object) -> None: - if is_point(a): - reveal_type(a) # N: Revealed type is "__main__.Point" - else: - reveal_type(a) # N: Revealed type is "builtins.object" -[builtins fixtures/tuple.pyi] - [case testTypeIsElif] from typing_extensions import TypeIs from typing import Union @@ -115,6 +104,15 @@ def main(x: object, type_check_func: Callable[[object], TypeIs[T]]) -> T: reveal_type(main("a", is_str)) # N: Revealed type is "builtins.str" [builtins fixtures/exception.pyi] +[case testTypeIsPassedAsTypeVarIsBool] +from typing import Callable, TypeVar +from typing_extensions import TypeIs +T = TypeVar('T') +def is_str(x: object) -> TypeIs[str]: pass +def main(f: Callable[[object], T]) -> T: pass +reveal_type(main(is_str)) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + [case testTypeIsUnionIn] from typing import Union from typing_extensions import TypeIs diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 9ca67376da26a..93d20eb26f6e5 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -1,5 +1,4 @@ [case testTypeVarDefaultsBasic] -import builtins from typing import Generic, TypeVar, ParamSpec, Callable, Tuple, List from typing_extensions import TypeVarTuple, Unpack @@ -10,7 +9,7 @@ Ts1 = TypeVarTuple("Ts1", default=Unpack[Tuple[int, str]]) def f1(a: T1) -> List[T1]: ... reveal_type(f1) # N: Revealed type is "def [T1 = builtins.int] (a: T1`-1 = builtins.int) -> builtins.list[T1`-1 = builtins.int]" -def f2(a: Callable[P1, None] ) -> Callable[P1, None]: ... +def f2(a: Callable[P1, None]) -> Callable[P1, None]: ... reveal_type(f2) # N: Revealed type is "def [P1 = [builtins.int, builtins.str]] (a: def (*P1.args, **P1.kwargs)) -> def (*P1.args, **P1.kwargs)" def f3(a: Tuple[Unpack[Ts1]]) -> Tuple[Unpack[Ts1]]: ... @@ -68,7 +67,7 @@ P2 = ParamSpec("P2", default=2) # E: The default argument to ParamSpec must be P3 = ParamSpec("P3", default=(2, int)) # E: The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec P4 = ParamSpec("P4", default=[2, int]) # E: Argument 0 of ParamSpec default must be a type -Ts1 = TypeVarTuple("Ts1", default=2) # E: The default argument to TypeVarTuple must be an Unpacked tuple +Ts1 = TypeVarTuple("Ts1", default=2) # E: The default argument to TypeVarTuple must be an Unpacked tuple Ts2 = TypeVarTuple("Ts2", default=int) # E: The default argument to TypeVarTuple must be an Unpacked tuple Ts3 = TypeVarTuple("Ts3", default=Tuple[int]) # E: The default argument to TypeVarTuple must be an Unpacked tuple [builtins fixtures/tuple.pyi] @@ -181,8 +180,8 @@ reveal_type(func_b1(callback1)) # N: Revealed type is "def (x: builtins.str)" reveal_type(func_b1(2)) # N: Revealed type is "def (builtins.int, builtins.str)" def func_c1(x: Union[int, Callable[[Unpack[Ts1]], None]]) -> Tuple[Unpack[Ts1]]: ... -# reveal_type(func_c1(callback1)) # Revealed type is "builtins.tuple[str]" # TODO -# reveal_type(func_c1(2)) # Revealed type is "builtins.tuple[builtins.int, builtins.str]" # TODO +# reveal_type(func_c1(callback1)) # Revealed type is "Tuple[str]" # TODO +reveal_type(func_c1(2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" [builtins fixtures/tuple.pyi] [case testTypeVarDefaultsClass1] @@ -717,3 +716,36 @@ def func_d3( reveal_type(c) # N: Revealed type is "__main__.B[__main__.A[builtins.dict[builtins.int, builtins.float]]]" reveal_type(d) # N: Revealed type is "__main__.B[builtins.int]" [builtins fixtures/dict.pyi] + +[case testTypeVarDefaultsAndTypeObjectTypeInUnion] +from __future__ import annotations +from typing import Generic +from typing_extensions import TypeVar + +_I = TypeVar("_I", default=int) + +class C(Generic[_I]): pass + +t: type[C] | int = C +[builtins fixtures/tuple.pyi] + + + +[case testGenericTypeAliasWithDefaultTypeVarPreservesNoneInDefault] +from typing_extensions import TypeVar +from typing import Generic, Union + +T1 = TypeVar("T1", default=Union[int, None]) +T2 = TypeVar("T2", default=Union[int, None]) + + +class A(Generic[T1, T2]): + def __init__(self, a: T1, b: T2) -> None: + self.a = a + self.b = b + + +MyA = A[T1, int] +a: MyA = A(None, 10) +reveal_type(a.a) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index ea692244597c2..f49e1b3c66132 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2460,3 +2460,30 @@ def test(x: T, *args: Unpack[Ts]) -> Tuple[T, Unpack[Ts]]: ... reveal_type(test) # N: Revealed type is "def [T, Ts] (builtins.list[T`2], *args: Unpack[Ts`-2]) -> __main__.CM[Tuple[T`2, Unpack[Ts`-2]]]" [builtins fixtures/tuple.pyi] + +[case testMixingTypeVarTupleAndParamSpec] +from typing import Generic, ParamSpec, TypeVarTuple, Unpack, Callable, TypeVar + +P = ParamSpec("P") +Ts = TypeVarTuple("Ts") + +class A(Generic[P, Unpack[Ts]]): ... +class B(Generic[Unpack[Ts], P]): ... + +a: A[[int, str], int, str] +reveal_type(a) # N: Revealed type is "__main__.A[[builtins.int, builtins.str], builtins.int, builtins.str]" +b: B[int, str, [int, str]] +reveal_type(b) # N: Revealed type is "__main__.B[builtins.int, builtins.str, [builtins.int, builtins.str]]" + +x: A[int, str, [int, str]] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" +reveal_type(x) # N: Revealed type is "__main__.A[Any, Unpack[builtins.tuple[Any, ...]]]" +y: B[[int, str], int, str] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "str" +reveal_type(y) # N: Revealed type is "__main__.B[Unpack[builtins.tuple[Any, ...]], Any]" + +R = TypeVar("R") +class C(Generic[P, R]): + fn: Callable[P, None] + +c: C[int, str] # E: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" +reveal_type(c.fn) # N: Revealed type is "def (*Any, **Any)" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index effaf620f1f0b..8b961d88d23d9 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -706,3 +706,29 @@ Func = Callable[[], T] class A: ... class B: ... + +[case testTypeCommentInGenericTypeWithConstrainedTypeVar] +from typing import Generic, TypeVar + +NT = TypeVar("NT", int, float) + +class Foo1(Generic[NT]): + p = 1 # type: int + +class Foo2(Generic[NT]): + p, q = 1, 2.0 # type: (int, float) + +class Foo3(Generic[NT]): + def bar(self) -> None: + p = 1 # type: int + +class Foo4(Generic[NT]): + def bar(self) -> None: + p, q = 1, 2.0 # type: (int, float) + +def foo3(x: NT) -> None: + p = 1 # type: int + +def foo4(x: NT) -> None: + p, q = 1, 2.0 # type: (int, float) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index cbad1bd5449ed..e6818ab5c3c71 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1000,6 +1000,16 @@ class Test4(Generic[T3]): [builtins fixtures/isinstancelist.pyi] +[case testUnreachableBlockStaysUnreachableWithTypeVarConstraints] +# flags: --always-false COMPILE_TIME_FALSE +from typing import TypeVar +COMPILE_TIME_FALSE = False +T = TypeVar("T", int, str) +def foo(x: T) -> T: + if COMPILE_TIME_FALSE: + return "bad" + return x + [case testUnreachableFlagContextManagersNoSuppress] # flags: --warn-unreachable from contextlib import contextmanager diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 2495a883aa715..bb0e80acee1e2 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -268,17 +268,19 @@ f(a, *(b, cc)) [builtins fixtures/tuple.pyi] [case testInvalidVarArg] -# flags: --no-strict-optional def f(a: 'A') -> None: pass class A: pass -a = None # type: A +a = A() -f(*None) -f(*a) # E: List or tuple expected as variadic arguments +f(*None) # E: Expected iterable as variadic argument +f(*a) # E: Expected iterable as variadic argument f(*(a,)) + +f(*4) # E: Expected iterable as variadic argument +f(a, *4) # E: Expected iterable as variadic argument [builtins fixtures/tuple.pyi] @@ -543,9 +545,9 @@ if int(): if int(): b, b = f(b, b, *aa) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" if int(): - a, b = f(a, *a) # E: List or tuple expected as variadic arguments + a, b = f(a, *a) # E: Expected iterable as variadic argument if int(): - a, b = f(*a) # E: List or tuple expected as variadic arguments + a, b = f(*a) # E: Expected iterable as variadic argument if int(): a, a = f(*aa) @@ -737,7 +739,7 @@ bar(*good1) bar(*good2) bar(*good3) bar(*bad1) # E: Argument 1 to "bar" has incompatible type "*I[str]"; expected "float" -bar(*bad2) # E: List or tuple expected as variadic arguments +bar(*bad2) # E: Expected iterable as variadic argument [builtins fixtures/dict.pyi] -- Keyword arguments unpacking diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 2262b7e7280c2..2bab19e0d42fe 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -153,18 +153,6 @@ FLAG = False if not FLAG: x = "unreachable" -[case testErrorContextConfig] -# cmd: mypy main.py -[file mypy.ini] -\[mypy] -show_error_context=True -[file main.py] -def f() -> None: - 0 + "" -[out] -main.py: note: In function "f": -main.py:2: error: Unsupported operand types for + ("int" and "str") - [case testAltConfigFile] # cmd: mypy --config-file config.ini main.py [file config.ini] @@ -176,43 +164,6 @@ FLAG = False if not FLAG: x = "unreachable" -[case testNoConfigFile] -# cmd: mypy main.py --config-file= -[file mypy.ini] -\[mypy] -warn_unused_ignores = True -[file main.py] -# type: ignore - -[case testPerFileConfigSection] -# cmd: mypy x.py y.py z.py -[file mypy.ini] -\[mypy] -disallow_untyped_defs = True -\[mypy-y] -disallow_untyped_defs = False -\[mypy-z] -disallow_untyped_calls = True -[file x.py] -def f(a): - pass -def g(a: int) -> int: - return f(a) -[file y.py] -def f(a): - pass -def g(a: int) -> int: - return f(a) -[file z.py] -def f(a): - pass -def g(a: int) -> int: - return f(a) -[out] -z.py:1: error: Function is missing a type annotation -z.py:4: error: Call to untyped function "f" in typed context -x.py:1: error: Function is missing a type annotation - [case testPerFileConfigSectionMultipleMatchesDisallowed] # cmd: mypy xx.py xy.py yx.py yy.py [file mypy.ini] @@ -326,43 +277,6 @@ file.py:1: error: Cannot find implementation or library stub for module named "n file.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports file.py:6: error: Argument 1 to "foo" has incompatible type "str"; expected "int" -[case testIgnoreErrorsConfig] -# cmd: mypy x.py y.py -[file mypy.ini] -\[mypy] -\[mypy-x] -ignore_errors = True -[file x.py] -x: str = 5 -[file y.py] -x: str = 5 -[out] -y.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") - -[case testConfigFollowImportsNormal] -# cmd: mypy main.py -[file main.py] -from a import x -x + 0 -x + '' # E -import a -a.x + 0 -a.x + '' # E -a.y # E -a + 0 # E -[file mypy.ini] -\[mypy] -follow_imports = normal -[file a.py] -x = 0 -x += '' # Error reported here -[out] -a.py:2: error: Unsupported operand types for + ("int" and "str") -main.py:3: error: Unsupported operand types for + ("int" and "str") -main.py:6: error: Unsupported operand types for + ("int" and "str") -main.py:7: error: Module has no attribute "y" -main.py:8: error: Unsupported operand types for + (Module and "int") - [case testConfigFollowImportsSysPath] # cmd: mypy main.py [file main.py] @@ -389,102 +303,6 @@ main.py:6: error: Unsupported operand types for + ("int" and "str") main.py:7: error: Module has no attribute "y" main.py:8: error: Unsupported operand types for + (Module and "int") -[case testConfigFollowImportsSilent] -# cmd: mypy main.py -[file main.py] -from a import x -x + '' -import a -a.x + '' -a.y -a + 0 -[file mypy.ini] -\[mypy] -follow_imports = silent -[file a.py] -x = 0 -x += '' # No error reported -[out] -main.py:2: error: Unsupported operand types for + ("int" and "str") -main.py:4: error: Unsupported operand types for + ("int" and "str") -main.py:5: error: Module has no attribute "y" -main.py:6: error: Unsupported operand types for + (Module and "int") - -[case testConfigFollowImportsSkip] -# cmd: mypy main.py -[file main.py] -from a import x -reveal_type(x) # Expect Any -import a -reveal_type(a.x) # Expect Any -[file mypy.ini] -\[mypy] -follow_imports = skip -[file a.py] -/ # No error reported -[out] -main.py:2: note: Revealed type is "Any" -main.py:4: note: Revealed type is "Any" -== Return code: 0 - -[case testConfigFollowImportsError] -# cmd: mypy main.py -[file main.py] -from a import x # Error reported here -reveal_type(x) # Expect Any -import a -reveal_type(a.x) # Expect Any -[file mypy.ini] -\[mypy] -follow_imports = error -[file a.py] -/ # No error reported -[out] -main.py:1: error: Import of "a" ignored -main.py:1: note: (Using --follow-imports=error, module not passed on command line) -main.py:2: note: Revealed type is "Any" -main.py:4: note: Revealed type is "Any" - -[case testConfigFollowImportsSelective] -# cmd: mypy main.py -[file mypy.ini] -\[mypy] -\[mypy-normal] -follow_imports = normal -\[mypy-silent] -follow_imports = silent -\[mypy-skip] -follow_imports = skip -\[mypy-error] -follow_imports = error -[file main.py] -import normal -import silent -import skip -import error -reveal_type(normal.x) -reveal_type(silent.x) -reveal_type(skip) -reveal_type(error) -[file normal.py] -x = 0 -x += '' -[file silent.py] -x = 0 -x += '' -[file skip.py] -bla bla -[file error.py] -bla bla -[out] -normal.py:2: error: Unsupported operand types for + ("int" and "str") -main.py:4: error: Import of "error" ignored -main.py:4: note: (Using --follow-imports=error, module not passed on command line) -main.py:5: note: Revealed type is "builtins.int" -main.py:6: note: Revealed type is "builtins.int" -main.py:7: note: Revealed type is "Any" -main.py:8: note: Revealed type is "Any" - [case testConfigFollowImportsInvalid] # cmd: mypy main.py [file mypy.ini] @@ -495,31 +313,6 @@ follow_imports =True mypy.ini: [mypy]: follow_imports: invalid choice 'True' (choose from 'normal', 'silent', 'skip', 'error') == Return code: 0 -[case testConfigSilentMissingImportsOff] -# cmd: mypy main.py -[file main.py] -import missing # Expect error here -reveal_type(missing.x) # Expect Any -[file mypy.ini] -\[mypy] -ignore_missing_imports = False -[out] -main.py:1: error: Cannot find implementation or library stub for module named "missing" -main.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -main.py:2: note: Revealed type is "Any" - -[case testConfigSilentMissingImportsOn] -# cmd: mypy main.py -[file main.py] -import missing # No error here -reveal_type(missing.x) # Expect Any -[file mypy.ini] -\[mypy] -ignore_missing_imports = True -[out] -main.py:2: note: Revealed type is "Any" -== Return code: 0 - [case testFailedImportOnWrongCWD] # cmd: mypy main.py # cwd: main/subdir1/subdir2 @@ -683,21 +476,10 @@ int_pow.py:11: note: Revealed type is "Any" python_version = 3.8 \[mypy-m] disallow_any_generics = True - [file m.py] -s = tuple([1, 2, 3]) # no error - -def f(t: tuple) -> None: pass -def g() -> list: pass -def h(s: dict) -> None: pass -def i(s: set) -> None: pass def j(s: frozenset) -> None: pass [out] -m.py:3: error: Implicit generic "Any". Use "typing.Tuple" and specify generic parameters -m.py:4: error: Implicit generic "Any". Use "typing.List" and specify generic parameters -m.py:5: error: Implicit generic "Any". Use "typing.Dict" and specify generic parameters -m.py:6: error: Implicit generic "Any". Use "typing.Set" and specify generic parameters -m.py:7: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generic parameters +m.py:1: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generic parameters [case testDisallowAnyGenericsTypingCollections] # cmd: mypy m.py @@ -705,21 +487,11 @@ m.py:7: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generi \[mypy] \[mypy-m] disallow_any_generics = True - [file m.py] -from typing import Tuple, List, Dict, Set, FrozenSet - -def f(t: Tuple) -> None: pass -def g() -> List: pass -def h(s: Dict) -> None: pass -def i(s: Set) -> None: pass +from typing import FrozenSet def j(s: FrozenSet) -> None: pass [out] -m.py:3: error: Missing type parameters for generic type "Tuple" -m.py:4: error: Missing type parameters for generic type "List" -m.py:5: error: Missing type parameters for generic type "Dict" -m.py:6: error: Missing type parameters for generic type "Set" -m.py:7: error: Missing type parameters for generic type "FrozenSet" +m.py:2: error: Missing type parameters for generic type "FrozenSet" [case testSectionInheritance] # cmd: mypy a @@ -756,18 +528,6 @@ ignore_errors = False a/b/c/d/e/__init__.py:2: error: Missing type parameters for generic type "List" a/b/c/d/e/__init__.py:3: error: Argument 1 to "g" has incompatible type "None"; expected "List[Any]" -[case testDisallowUntypedDefsAndGenerics] -# cmd: mypy a.py -[file mypy.ini] -\[mypy] -disallow_untyped_defs = True -disallow_any_generics = True -[file a.py] -def get_tasks(self): - return 'whatever' -[out] -a.py:1: error: Function is missing a return type annotation - [case testMissingFile] # cmd: mypy nope.py [out] @@ -775,26 +535,6 @@ mypy: can't read file 'nope.py': No such file or directory == Return code: 2 --' -[case testParseError] -# cmd: mypy a.py -[file a.py] -def foo( -[out] -a.py:1: error: unexpected EOF while parsing -== Return code: 2 -[out version>=3.10] -a.py:1: error: '(' was never closed -== Return code: 2 - -[case testParseErrorAnnots] -# cmd: mypy a.py -[file a.py] -def foo(x): - # type: (str, int) -> None - return -[out] -a.py:1: error: Type signature has too many arguments - [case testModulesAndPackages] # cmd: mypy --package p.a --package p.b --module c [file p/__init__.py] @@ -914,6 +654,23 @@ s4.py:2: error: Incompatible return value type (got "int", expected "str") s3.py:2: error: Incompatible return value type (got "List[int]", expected "int") s1.py:2: error: Incompatible return value type (got "int", expected "str") +[case testShadowFileWithPretty] +# cmd: mypy a.py --pretty --shadow-file a.py b.py +[file a.py] +b: bytes +[file b.py] +a: int = "" +b: bytes = 1 +[out] +a.py:1: error: Incompatible types in assignment (expression has type "str", +variable has type "int") + a: int = "" + ^~ +a.py:2: error: Incompatible types in assignment (expression has type "int", +variable has type "bytes") + b: bytes = 1 + ^ + [case testConfigWarnUnusedSection1] # cmd: mypy foo.py quux.py spam/eggs.py [file mypy.ini] @@ -1472,15 +1229,6 @@ pass Warning: --new-type-inference flag is deprecated; new type inference algorithm is already enabled by default == Return code: 0 -[case testNotesOnlyResultInExitSuccess] -# cmd: mypy a.py -[file a.py] -def f(): - x: int = "no" -[out] -a.py:2: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs -== Return code: 0 - [case testCustomTypeshedDirFilePassedExplicitly] # cmd: mypy --custom-typeshed-dir dir m.py dir/stdlib/foo.pyi [file m.py] diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index ca2c969d2f5e9..7dfddd8f74df5 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -263,6 +263,38 @@ mypy-daemon: error: Missing target module, package, files, or command. $ dmypy stop Daemon stopped +[case testDaemonRunTwoFilesFullTypeshed] +$ dmypy run x.py +Daemon started +Success: no issues found in 1 source file +$ dmypy run y.py +Success: no issues found in 1 source file +$ dmypy run x.py +Success: no issues found in 1 source file +[file x.py] +[file y.py] + +[case testDaemonCheckTwoFilesFullTypeshed] +$ dmypy start +Daemon started +$ dmypy check foo.py +foo.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy check bar.py +Success: no issues found in 1 source file +$ dmypy check foo.py +foo.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +[file foo.py] +from bar import add +x: str = add("a", "b") +x_error: int = add("a", "b") +[file bar.py] +def add(a, b) -> str: + return a + b + [case testDaemonWarningSuccessExitCode-posix] $ dmypy run -- foo.py --follow-imports=error --python-version=3.11 Daemon started diff --git a/test-data/unit/deps-classes.test b/test-data/unit/deps-classes.test index ebe2e9caed026..a8fc5d6294917 100644 --- a/test-data/unit/deps-classes.test +++ b/test-data/unit/deps-classes.test @@ -178,6 +178,7 @@ def g() -> None: A.X [file m.py] class B: pass +[builtins fixtures/enum.pyi] [out] -> m.g -> , m.A, m.f, m.g diff --git a/test-data/unit/deps-types.test b/test-data/unit/deps-types.test index def117fe04df0..6992a5bdec002 100644 --- a/test-data/unit/deps-types.test +++ b/test-data/unit/deps-types.test @@ -905,6 +905,7 @@ def g() -> None: A.X [file mod.py] class B: pass +[builtins fixtures/tuple.pyi] [out] -> m.g -> , m.f, m.g diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 3364dee6c6964..84cea99bf2f62 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1433,7 +1433,7 @@ class B(A): -> m [case testPEP695TypeAliasDeps] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 from a import C, E type A = C type A2 = A diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index 9212d902e8b21..4acf451e2c348 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -566,6 +566,7 @@ A = Enum('A', 'x') B = Enum('B', 'y') C = IntEnum('C', 'x') D = IntEnum('D', 'x y') +[builtins fixtures/enum.pyi] [out] __main__.B.x __main__.B.y @@ -605,6 +606,7 @@ class D(Enum): Y = 'b' class F(Enum): X = 0 +[builtins fixtures/enum.pyi] [out] __main__.B.Y __main__.B.Z @@ -1532,7 +1534,7 @@ __main__.C.get_by_team_and_id __main__.Optional [case testPEP695TypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 from typing_extensions import TypeAlias, TypeAliasType type A = int type B = str @@ -1544,7 +1546,7 @@ G = TypeAliasType("G", int) type H = int [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 from typing_extensions import TypeAlias, TypeAliasType type A = str type B = str @@ -1566,7 +1568,7 @@ __main__.G __main__.H [case testPEP695TypeAlias2] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 type A[T: int] = list[T] type B[T: int] = list[T] type C[T: (int, str)] = list[T] @@ -1575,7 +1577,7 @@ type E[T: int] = list[T] type F[T: (int, str)] = list[T] [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 type A[T] = list[T] type B[T: str] = list[T] type C[T: (int, None)] = list[T] @@ -1590,13 +1592,13 @@ __main__.C __main__.D [case testPEP695GenericFunction] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 def f[T](x: T) -> T: return x def g[T](x: T, y: T) -> T: return x [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 def f[T](x: T) -> T: return x def g[T, S](x: T, y: S) -> S: @@ -1605,7 +1607,7 @@ def g[T, S](x: T, y: S) -> S: __main__.g [case testPEP695GenericClass] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 class C[T]: pass class D[T]: @@ -1615,7 +1617,7 @@ class E[T]: class F[T]: def f(self, x: object) -> T: ... [file next.py] -# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12 +# flags: --python-version=3.12 class C[T]: pass class D[T: int]: diff --git a/test-data/unit/fine-grained-python312.test b/test-data/unit/fine-grained-python312.test index 3970c8cacfbf7..2cb2148a66fef 100644 --- a/test-data/unit/fine-grained-python312.test +++ b/test-data/unit/fine-grained-python312.test @@ -1,5 +1,4 @@ [case testPEP695TypeAliasDep] -# flags: --enable-incomplete-feature=NewGenericSyntax import m def g() -> m.C: return m.f() @@ -15,10 +14,9 @@ def f() -> int: pass [out] == -main:4: error: Incompatible return value type (got "int", expected "str") +main:3: error: Incompatible return value type (got "int", expected "str") [case testPEP695ChangeOldStyleToNewStyleTypeAlias] -# flags: --enable-incomplete-feature=NewGenericSyntax from m import A A() @@ -31,10 +29,9 @@ type A = int [builtins fixtures/tuple.pyi] [out] == -main:3: error: "TypeAliasType" not callable +main:2: error: "TypeAliasType" not callable [case testPEP695VarianceChangesDueToDependency] -# flags: --enable-incomplete-feature=NewGenericSyntax from a import C x: C[object] = C[int]() @@ -55,10 +52,9 @@ class A[T]: [out] == -main:4: error: Incompatible types in assignment (expression has type "C[int]", variable has type "C[object]") +main:3: error: Incompatible types in assignment (expression has type "C[int]", variable has type "C[object]") [case testPEP695TypeAliasChangesDueToDependency] -# flags: --enable-incomplete-feature=NewGenericSyntax from a import A x: A x = 0 @@ -78,5 +74,44 @@ from builtins import tuple as B [typing fixtures/typing-full.pyi] [out] == -main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]") -main:5: error: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, str]") +main:3: error: Incompatible types in assignment (expression has type "int", variable has type "Tuple[int, str]") +main:4: error: Incompatible types in assignment (expression has type "str", variable has type "Tuple[int, str]") + +[case testPEP695NestedGenericClassMethodUpdated] +from a import f + +class C: + class D[T]: + x: T + def m(self) -> T: + f() + return self.x + +[file a.py] +def f() -> None: pass + +[file a.py.2] +def f(x: int) -> None: pass +[out] +== +main:7: error: Missing positional argument "x" in call to "f" + +[case testPEP695MultipleNestedGenericClassMethodUpdated] +from a import f + +class A: + class C: + class D[T]: + x: T + def m(self) -> T: + f() + return self.x + +[file a.py] +def f() -> None: pass + +[file a.py.2] +def f(x: int) -> None: pass +[out] +== +main:8: error: Missing positional argument "x" in call to "f" diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 2a652e50b1e6d..9ff8a37ae9aee 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -3705,6 +3705,34 @@ def foo() -> None: == b.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") +[case testTypedDictUpdateReadOnly] +import b +[file a.py] +from typing_extensions import TypedDict, ReadOnly +Point = TypedDict('Point', {'x': int, 'y': int}) +p = Point(x=1, y=2) +[file a.py.2] +from typing_extensions import TypedDict, ReadOnly +class Point(TypedDict): + x: int + y: ReadOnly[int] +p = Point(x=1, y=2) +[file a.py.3] +from typing_extensions import TypedDict, ReadOnly +Point = TypedDict('Point', {'x': ReadOnly[int], 'y': int}) +p = Point(x=1, y=2) +[file b.py] +from a import Point +def foo(x: Point) -> None: + x['x'] = 1 + x['y'] = 2 +[builtins fixtures/dict.pyi] +[out] +== +b.py:4: error: ReadOnly TypedDict key "y" TypedDict is mutated +== +b.py:3: error: ReadOnly TypedDict key "x" TypedDict is mutated + [case testBasicAliasUpdate] import b [file a.py] @@ -5327,6 +5355,7 @@ c: C c = C.X if int(): c = 1 +[builtins fixtures/enum.pyi] [out] == == @@ -5358,6 +5387,7 @@ if int(): n = C.X if int(): n = c +[builtins fixtures/enum.pyi] [out] == == @@ -5382,6 +5412,7 @@ from enum import Enum class C(Enum): X = 0 +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == @@ -5404,6 +5435,7 @@ from enum import Enum class C(Enum): X = 0 Y = 1 +[builtins fixtures/enum.pyi] [out] == a.py:4: error: Argument 1 to "f" has incompatible type "C"; expected "int" @@ -5428,6 +5460,7 @@ c: C c = C.X if int(): c = 1 +[builtins fixtures/tuple.pyi] [out] == == @@ -5457,6 +5490,7 @@ if int(): n: int n = C.X n = c +[builtins fixtures/enum.pyi] [out] == == @@ -5478,6 +5512,7 @@ C = Enum('C', 'X Y') from enum import Enum C = Enum('C', 'X') +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == @@ -5498,6 +5533,7 @@ class C: [file b.py.2] from enum import Enum C = Enum('C', [('X', 0), ('Y', 1)]) +[builtins fixtures/tuple.pyi] [out] == a.py:4: error: Argument 1 to "f" has incompatible type "C"; expected "int" @@ -10497,3 +10533,631 @@ from pkg.sub import modb [out] == + +[case testFineGrainedFunctoolsPartial] +import m + +[file m.py] +from typing import Callable +from partial import p1 + +reveal_type(p1) +p1("a") +p1("a", 3) +p1("a", c=3) +p1(1, 3) +p1(1, "a", 3) +p1(a=1, b="a", c=3) +[builtins fixtures/dict.pyi] + +[file partial.py] +from typing import Callable +import functools + +def foo(a: int, b: str, c: int = 5) -> int: ... +p1 = foo + +[file partial.py.2] +from typing import Callable +import functools + +def foo(a: int, b: str, c: int = 5) -> int: ... +p1 = functools.partial(foo, 1) + +[out] +m.py:4: note: Revealed type is "def (a: builtins.int, b: builtins.str, c: builtins.int =) -> builtins.int" +m.py:5: error: Too few arguments +m.py:5: error: Argument 1 has incompatible type "str"; expected "int" +m.py:6: error: Argument 1 has incompatible type "str"; expected "int" +m.py:6: error: Argument 2 has incompatible type "int"; expected "str" +m.py:7: error: Too few arguments +m.py:7: error: Argument 1 has incompatible type "str"; expected "int" +m.py:8: error: Argument 2 has incompatible type "int"; expected "str" +== +m.py:4: note: Revealed type is "functools.partial[builtins.int]" +m.py:8: error: Argument 1 to "foo" has incompatible type "int"; expected "str" +m.py:9: error: Too many arguments for "foo" +m.py:9: error: Argument 1 to "foo" has incompatible type "int"; expected "str" +m.py:9: error: Argument 2 to "foo" has incompatible type "str"; expected "int" +m.py:10: error: Unexpected keyword argument "a" for "foo" +partial.py:4: note: "foo" defined here + +[case testReplaceFunctionWithDecoratedFunctionIndirect] +from b import f +x: int = f() +import b +y: int = b.f() + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +from typing import Callable +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testReplaceFunctionWithDecoratedFunctionIndirect2] +from c import f +x: int = f() +import c +y: int = c.f() + +[file c.py] +from b import f + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +from typing import Callable +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testReplaceFunctionWithClassIndirect] +from b import f +x: int = f() +import b +y: int = b.f() + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +class f: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "f", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "f", variable has type "int") + +[case testReplaceFunctionWithClassIndirect2] +from c import f +x: int = f() +import c +y: int = c.f() + +[file c.py] +from b import f + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +class f: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: Incompatible types in assignment (expression has type "f", variable has type "int") +main:4: error: Incompatible types in assignment (expression has type "f", variable has type "int") + + +[case testDeprecatedAddKeepChangeAndRemoveFunctionDeprecation] +# flags: --enable-error-code=deprecated + +from a import f +f() +import a +a.f() + +[file a.py] +def f() -> None: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.3] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.4] +from typing_extensions import deprecated +@deprecated("use f3 instead") +def f() -> None: ... + +[file a.py.5] +def f() -> None: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:3: error: function a.f is deprecated: use f2 instead +main:6: error: function a.f is deprecated: use f2 instead +== +main:3: error: function a.f is deprecated: use f2 instead +main:6: error: function a.f is deprecated: use f2 instead +== +main:3: error: function a.f is deprecated: use f3 instead +main:6: error: function a.f is deprecated: use f3 instead +== + + +[case testDeprecatedRemoveFunctionDeprecation] +# flags: --enable-error-code=deprecated +from a import f +f() +import a +a.f() + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.2] +def f() -> None: ... + +[builtins fixtures/tuple.pyi] +[out] +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead +== + +[case testDeprecatedKeepFunctionDeprecation] +# flags: --enable-error-code=deprecated +from a import f +f() +import a +a.f() + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> None: ... + +[builtins fixtures/tuple.pyi] +[out] +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead +== +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead + + +[case testDeprecatedAddFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated +from b import f +f() +import b +b.f() + +[file b.py] +from a import f + +[file a.py] +def f() -> int: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> int: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: error: function a.f is deprecated: use f2 instead +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead + + +[case testDeprecatedChangeFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated +from b import f +f() +import b +b.f() + +[file b.py] +from a import f + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f1 instead") +def f() -> int: ... + +[file a.py.2] +from typing_extensions import deprecated +@deprecated("use f2 instead") +def f() -> int: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: error: function a.f is deprecated: use f1 instead +main:2: error: function a.f is deprecated: use f1 instead +main:5: error: function a.f is deprecated: use f1 instead +== +b.py:1: error: function a.f is deprecated: use f2 instead +main:2: error: function a.f is deprecated: use f2 instead +main:5: error: function a.f is deprecated: use f2 instead + +[case testDeprecatedRemoveFunctionDeprecationIndirectImport] +# flags: --enable-error-code=deprecated +from b import f +f() +import b +b.f() + +[file b.py] +from a import f + +[file a.py] +from typing_extensions import deprecated +@deprecated("use f1 instead") +def f() -> int: ... + +[file a.py.2] +def f() -> int: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: error: function a.f is deprecated: use f1 instead +main:2: error: function a.f is deprecated: use f1 instead +main:5: error: function a.f is deprecated: use f1 instead +== + + +[case testDeprecatedFunctionAlreadyDecorated1-only_when_cache] +# flags: --enable-error-code=deprecated +from b import f +x: str = f() +import b +y: str = b.f() + +[file b.py] +from a import f + +[file a.py] +from typing import Callable + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[file a.py.2] +from typing import Callable +from typing_extensions import deprecated + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@deprecated("deprecated decorated function") +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: error: function a.f is deprecated: deprecated decorated function +main:2: error: function a.f is deprecated: deprecated decorated function +main:5: error: function a.f is deprecated: deprecated decorated function + + +[case testDeprecatedFunctionAlreadyDecorated2-only_when_nocache] +# flags: --enable-error-code=deprecated +from b import f +x: str = f() +import b +y: str = b.f() + +[file b.py] +from a import f + +[file a.py] +from typing import Callable + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@d +def f() -> str: ... + +[file a.py.2] +from typing import Callable +from typing_extensions import deprecated + +def d(t: Callable[[], str]) -> Callable[[], str]: ... + +@deprecated("deprecated decorated function") +@d +def f() -> str: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: function a.f is deprecated: deprecated decorated function +main:5: error: function a.f is deprecated: deprecated decorated function +b.py:1: error: function a.f is deprecated: deprecated decorated function + + +[case testDeprecatedAddClassDeprecationIndirectImport1-only_when_cache] +# flags: --enable-error-code=deprecated +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +class C: ... +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead + + +[case testDeprecatedAddClassDeprecationIndirectImport2-only_when_nocache] +# flags: --enable-error-code=deprecated +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +class C: ... +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead + + +[case testDeprecatedChangeClassDeprecationIndirectImport] +# flags: --enable-error-code=deprecated +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing_extensions import deprecated + +@deprecated("use C1 instead") +class C: ... +@deprecated("use D1 instead") +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: error: class a.C is deprecated: use C1 instead +b.py:2: error: class a.D is deprecated: use D1 instead +main:2: error: class a.C is deprecated: use C1 instead +main:6: error: class a.D is deprecated: use D1 instead +main:7: error: class a.D is deprecated: use D1 instead +== +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead + + +[case testDeprecatedRemoveClassDeprecationIndirectImport] +# flags: --enable-error-code=deprecated +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing_extensions import deprecated + +@deprecated("use C1 instead") +class C: ... +@deprecated("use D1 instead") +class D: ... + +[file a.py.2] +class C: ... +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +b.py:1: error: class a.C is deprecated: use C1 instead +b.py:2: error: class a.D is deprecated: use D1 instead +main:2: error: class a.C is deprecated: use C1 instead +main:6: error: class a.D is deprecated: use D1 instead +main:7: error: class a.D is deprecated: use D1 instead +== + + +[case testDeprecatedAddClassDeprecationIndirectImportAlreadyDecorated1-only_when_cache] +# flags: --enable-error-code=deprecated +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing import TypeVar + +T = TypeVar("T") +def dec(x: T) -> T: ... + +@dec +class C: ... +@dec +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead + + +[case testDeprecatedAddClassDeprecationIndirectImportAlreadyDecorated2-only_when_nocache] +# flags: --enable-error-code=deprecated +from b import C +x: C +C() +import b +y: b.D +b.D() + +[file b.py] +from a import C +from a import D + +[file a.py] +from typing import TypeVar + +T = TypeVar("T") +def dec(x: T) -> T: ... + +@dec +class C: ... +@dec +class D: ... + +[file a.py.2] +from typing_extensions import deprecated + +@deprecated("use C2 instead") +class C: ... + +@deprecated("use D2 instead") +class D: ... + +[builtins fixtures/tuple.pyi] +[out] +== +main:2: error: class a.C is deprecated: use C2 instead +main:6: error: class a.D is deprecated: use D2 instead +main:7: error: class a.D is deprecated: use D2 instead +b.py:1: error: class a.C is deprecated: use C2 instead +b.py:2: error: class a.D is deprecated: use D2 instead diff --git a/test-data/unit/fixtures/enum.pyi b/test-data/unit/fixtures/enum.pyi new file mode 100644 index 0000000000000..135e9cd16e7ce --- /dev/null +++ b/test-data/unit/fixtures/enum.pyi @@ -0,0 +1,18 @@ +# Minimal set of builtins required to work with Enums +from typing import TypeVar, Generic + +T = TypeVar('T') + +class object: + def __init__(self): pass + +class type: pass +class tuple(Generic[T]): + def __getitem__(self, x: int) -> T: pass + +class int: pass +class str: + def __len__(self) -> int: pass + +class dict: pass +class ellipsis: pass diff --git a/test-data/unit/fixtures/for.pyi b/test-data/unit/fixtures/for.pyi index 694f83e940b2c..10f45e68cd7da 100644 --- a/test-data/unit/fixtures/for.pyi +++ b/test-data/unit/fixtures/for.pyi @@ -12,9 +12,11 @@ class type: pass class tuple(Generic[t]): def __iter__(self) -> Iterator[t]: pass class function: pass +class ellipsis: pass class bool: pass class int: pass # for convenience -class str: pass # for convenience +class str: # for convenience + def upper(self) -> str: ... class list(Iterable[t], Generic[t]): def __iter__(self) -> Iterator[t]: pass diff --git a/test-data/unit/fixtures/notimplemented.pyi b/test-data/unit/fixtures/notimplemented.pyi index 2ca376ea07605..92edf84a7fd11 100644 --- a/test-data/unit/fixtures/notimplemented.pyi +++ b/test-data/unit/fixtures/notimplemented.pyi @@ -1,6 +1,5 @@ # builtins stub used in NotImplemented related cases. -from typing import Any, cast - +from typing import Any class object: def __init__(self) -> None: pass @@ -10,5 +9,10 @@ class function: pass class bool: pass class int: pass class str: pass -NotImplemented = cast(Any, None) class dict: pass + +class _NotImplementedType(Any): + __call__: NotImplemented # type: ignore +NotImplemented: _NotImplementedType + +class BaseException: pass diff --git a/test-data/unit/fixtures/staticmethod.pyi b/test-data/unit/fixtures/staticmethod.pyi index 8a87121b2a71d..a0ca831c7527e 100644 --- a/test-data/unit/fixtures/staticmethod.pyi +++ b/test-data/unit/fixtures/staticmethod.pyi @@ -19,3 +19,4 @@ class str: pass class bytes: pass class ellipsis: pass class dict: pass +class tuple: pass diff --git a/test-data/unit/fixtures/tuple-simple.pyi b/test-data/unit/fixtures/tuple-simple.pyi index 6c816c1c5b7a1..07f9edf63cdd0 100644 --- a/test-data/unit/fixtures/tuple-simple.pyi +++ b/test-data/unit/fixtures/tuple-simple.pyi @@ -5,7 +5,7 @@ from typing import Iterable, TypeVar, Generic -T = TypeVar('T') +T = TypeVar('T', covariant=True) class object: def __init__(self): pass diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index d136ac4ab8bee..7e9c642cf261a 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -26,6 +26,7 @@ TypedDict = 0 NoReturn = 0 Required = 0 NotRequired = 0 +ReadOnly = 0 Self = 0 T = TypeVar('T') @@ -59,6 +60,10 @@ class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): def __len__(self) -> int: ... def __contains__(self, arg: object) -> int: pass +class MutableMapping(Mapping[T, T_co], Generic[T, T_co], metaclass=ABCMeta): + # Other methods are not used in tests. + def clear(self) -> None: ... + # Fallback type for all typed dicts (does not exist at runtime). class _TypedDict(Mapping[str, object]): # Needed to make this class non-abstract. It is explicitly declared abstract in diff --git a/test-data/unit/lib-stub/enum.pyi b/test-data/unit/lib-stub/enum.pyi index 0e0b8e025d9f2..ccb3818b9d25d 100644 --- a/test-data/unit/lib-stub/enum.pyi +++ b/test-data/unit/lib-stub/enum.pyi @@ -1,4 +1,5 @@ from typing import Any, TypeVar, Union, Type, Sized, Iterator +from typing_extensions import Literal _T = TypeVar('_T') @@ -7,6 +8,7 @@ class EnumMeta(type, Sized): def __iter__(self: Type[_T]) -> Iterator[_T]: pass def __reversed__(self: Type[_T]) -> Iterator[_T]: pass def __getitem__(self: Type[_T], name: str) -> _T: pass + def __bool__(self) -> Literal[True]: pass class Enum(metaclass=EnumMeta): def __new__(cls: Type[_T], value: object) -> _T: pass diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index b5bfc1ab3f205..cb054b0e6b4fc 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -41,6 +41,9 @@ TypeVarTuple: _SpecialForm Unpack: _SpecialForm Required: _SpecialForm NotRequired: _SpecialForm +ReadOnly: _SpecialForm + +Self: _SpecialForm @final class TypeAliasType: diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test index 19b1839f86c0d..a6a64c75b2a3a 100644 --- a/test-data/unit/merge.test +++ b/test-data/unit/merge.test @@ -1490,6 +1490,7 @@ from enum import Enum class A(Enum): X = 0 Y = 1 +[builtins fixtures/enum.pyi] [out] TypeInfo<0>( Name(target.A) diff --git a/test-data/unit/parse-errors.test b/test-data/unit/parse-errors.test index c6b1c00a6169b..7b1078d3fa2f8 100644 --- a/test-data/unit/parse-errors.test +++ b/test-data/unit/parse-errors.test @@ -151,6 +151,13 @@ x = 0 # type: A B #comment #7 [out] file:2: error: Syntax error in type comment "A B" +[case testMissingBracket] +def foo( +[out] +file:1: error: unexpected EOF while parsing +[out version>=3.10] +file:1: error: '(' was never closed + [case testInvalidSignatureInComment1] def f(): # type: x pass diff --git a/test-data/unit/parse-python312.test b/test-data/unit/parse-python312.test index 90ee96f38deb4..2b1f9b42e0f78 100644 --- a/test-data/unit/parse-python312.test +++ b/test-data/unit/parse-python312.test @@ -1,5 +1,5 @@ [case testPEP695TypeAlias] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment type A[T] = C[T] [out] MypyFile:1( @@ -15,7 +15,7 @@ MypyFile:1( NameExpr(T))))))) [case testPEP695GenericFunction] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment def f[T](): pass def g[T: str](): pass @@ -46,7 +46,7 @@ MypyFile:1( PassStmt:5()))) [case testPEP695ParamSpec] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment def f[**P](): pass class C[T: int, **P]: pass @@ -68,7 +68,7 @@ MypyFile:1( PassStmt:4())) [case testPEP695TypeVarTuple] -# mypy: enable-incomplete-feature=NewGenericSyntax +# comment def f[*Ts](): pass class C[T: int, *Ts]: pass diff --git a/test-data/unit/parse-python313.test b/test-data/unit/parse-python313.test new file mode 100644 index 0000000000000..efbafb0766f59 --- /dev/null +++ b/test-data/unit/parse-python313.test @@ -0,0 +1,80 @@ +[case testPEP696TypeAlias] +type A[T = int] = C[T] +[out] +MypyFile:1( + TypeAliasStmt:1( + NameExpr(A) + TypeParam( + T + Default( + int?)) + LambdaExpr:1( + Block:-1( + ReturnStmt:1( + IndexExpr:1( + NameExpr(C) + NameExpr(T))))))) + +[case testPEP696GenericFunction] +def f[T = int](): pass +class C[T = int]: pass +[out] +MypyFile:1( + FuncDef:1( + f + TypeParam( + T + Default( + int?)) + Block:1( + PassStmt:1())) + ClassDef:2( + C + TypeParam( + T + Default( + int?)) + PassStmt:2())) + +[case testPEP696ParamSpec] +def f[**P = [int, str]](): pass +class C[**P = [int, str]]: pass +[out] +[out] +MypyFile:1( + FuncDef:1( + f + TypeParam( + **P + Default( + )) + Block:1( + PassStmt:1())) + ClassDef:2( + C + TypeParam( + **P + Default( + )) + PassStmt:2())) + +[case testPEP696TypeVarTuple] +def f[*Ts = *tuple[str, int]](): pass +class C[*Ts = *tuple[str, int]]: pass +[out] +MypyFile:1( + FuncDef:1( + f + TypeParam( + *Ts + Default( + Unpack[tuple?[str?, int?]])) + Block:1( + PassStmt:1())) + ClassDef:2( + C + TypeParam( + *Ts + Default( + Unpack[tuple?[str?, int?]])) + PassStmt:2())) diff --git a/test-data/unit/pep561.test b/test-data/unit/pep561.test index 9969c2894c36e..fb303a8fb5ec8 100644 --- a/test-data/unit/pep561.test +++ b/test-data/unit/pep561.test @@ -187,6 +187,13 @@ b.bf(1) testNamespacePkgWStubsWithNamespacePackagesFlag.py:7: error: Argument 1 to "bf" has incompatible type "int"; expected "bool" testNamespacePkgWStubsWithNamespacePackagesFlag.py:8: error: Argument 1 to "bf" has incompatible type "int"; expected "bool" +[case testMissingPytypedFlag] +# pkgs: typedpkg_ns_b +# flags: --namespace-packages --follow-untyped-imports +import typedpkg_ns.b.bbb as b +b.bf("foo", "bar") +[out] +testMissingPytypedFlag.py:4: error: Too many arguments for "bf" [case testTypedPkgNamespaceRegFromImportTwiceMissing] # pkgs: typedpkg_ns_a diff --git a/test-data/unit/plugins/attrhook2.py b/test-data/unit/plugins/attrhook2.py index 2d41a0fdf52f7..1ce318d2057b1 100644 --- a/test-data/unit/plugins/attrhook2.py +++ b/test-data/unit/plugins/attrhook2.py @@ -12,6 +12,8 @@ def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type return magic_field_callback if fullname == "m.Magic.nonexistent_field": return nonexistent_field_callback + if fullname == "m.Magic.no_assignment_field": + return no_assignment_field_callback return None @@ -24,5 +26,12 @@ def nonexistent_field_callback(ctx: AttributeContext) -> Type: return AnyType(TypeOfAny.from_error) +def no_assignment_field_callback(ctx: AttributeContext) -> Type: + if ctx.is_lvalue: + ctx.api.fail(f"Cannot assign to field", ctx.context) + return AnyType(TypeOfAny.from_error) + return ctx.default_attr_type + + def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 222430c3ef550..397ccbb0d3f88 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -11,138 +11,97 @@ print('hello, world') [out] hello, world -[case testReversed] +[case testMiscStdlibFeatures] +# Various legacy tests merged together to speed up test runtimes. + +def f(x: object) -> None: pass + +# testReversed from typing import Reversible -class A(Reversible): +class R(Reversible): def __iter__(self): return iter('oof') def __reversed__(self): return iter('foo') -print(list(reversed(range(5)))) -print(list(reversed([1,2,3]))) -print(list(reversed('abc'))) -print(list(reversed(A()))) -[out] --- Escape bracket at line beginning -\[4, 3, 2, 1, 0] -\[3, 2, 1] -\['c', 'b', 'a'] -\['f', 'o', 'o'] - -[case testIntAndFloatConversion] +f(list(reversed(range(5)))) +f(list(reversed([1,2,3]))) +f(list(reversed('abc'))) +f(list(reversed(R()))) + +# testIntAndFloatConversion from typing import SupportsInt, SupportsFloat class A(SupportsInt): def __int__(self): return 5 class B(SupportsFloat): def __float__(self): return 1.2 -print(int(1)) -print(int(6.2)) -print(int('3')) -print(int(b'4')) -print(int(A())) -print(float(-9)) -print(float(B())) -[out] -1 -6 -3 -4 -5 --9.0 -1.2 - -[case testAbs] +f(int(1)) +f(int(6.2)) +f(int('3')) +f(int(b'4')) +f(int(A())) +f(float(-9)) +f(float(B())) + +# testAbs from typing import SupportsAbs -class A(SupportsAbs[float]): +class Ab(SupportsAbs[float]): def __abs__(self) -> float: return 5.5 -print(abs(-1)) -print(abs(-1.2)) -print(abs(A())) -[out] -1 -1.2 -5.5 +f(abs(-1)) +f(abs(-1.2)) +f(abs(Ab())) -[case testAbs2] -n: int -f: float -n = abs(1) -abs(1) + 'x' # Error -f = abs(1.1) -abs(1.1) + 'x' # Error -[out] -_program.py:4: error: Unsupported operand types for + ("int" and "str") -_program.py:6: error: Unsupported operand types for + ("float" and "str") - -[case testRound] +# testRound from typing import SupportsRound -class A(SupportsRound): +class Ro(SupportsRound): def __round__(self, ndigits=0): return 'x%d' % ndigits -print(round(1.6)) -print(round(A())) -print(round(A(), 2)) -[out] -2 -x0 -x2 +f(round(1.6)) +f(round(Ro())) +f(round(Ro(), 2)) -[case testCallMethodViaTypeObject] -import typing -print(list.__add__([1, 2], [3, 4])) -[out] -\[1, 2, 3, 4] +# testCallMethodViaTypeObject +list.__add__([1, 2], [3, 4]) -[case testInheritedClassAttribute] +# testInheritedClassAttribute import typing -class A: +class AA: x = 1 - def f(self: typing.Optional["A"]) -> None: print('f') -class B(A): + def f(self: typing.Optional["AA"]) -> None: pass +class BB(AA): pass -B.f(None) -print(B.x) -[out] -f -1 - -[case testModuleAttributes] -import math -import typing -print(type(__spec__)) -print(math.__name__) -print(math.__spec__.name) -print(type(math.__dict__)) -print(type(math.__doc__ or '')) -print(type(math.__spec__).__name__) -print(math.__class__) -[out] - -math -math - - -ModuleSpec - +BB.f(None) +f(BB.x) -[case testSpecialAttributes] -import typing -class A: +# testSpecialAttributes +class Doc: """A docstring!""" -print(A().__doc__) -print(A().__class__) -[out] -A docstring! - +f(Doc().__doc__) +f(Doc().__class__) -[case testFunctionAttributes] -import typing -ord.__class__ -print(type(ord.__doc__ or '' + '')) -print(ord.__name__) -print(ord.__module__) +# testFunctionAttributes +f(ord.__class__) +f(type(ord.__doc__ or '' + '')) +f(ord.__name__) +f(ord.__module__) + +# testModuleAttributes +import math +f(type(__spec__)) +f(math.__name__) +f(math.__spec__.name) +f(type(math.__dict__)) +f(type(math.__doc__ or '')) +f(type(math.__spec__).__name__) +f(math.__class__) + +[case testAbs2] +n: int +f: float +n = abs(1) +abs(1) + 'x' # Error +f = abs(1.1) +abs(1.1) + 'x' # Error [out] - -ord -builtins +_program.py:4: error: Unsupported operand types for + ("int" and "str") +_program.py:6: error: Unsupported operand types for + ("float" and "str") [case testTypeAttributes] import typing @@ -274,9 +233,7 @@ def bin(f: IO[bytes]) -> None: txt(sys.stdout) bin(sys.stdout) [out] -_program.py:5: error: No overload variant of "write" of "IO" matches argument type "bytes" -_program.py:5: note: Possible overload variants: -_program.py:5: note: def write(self, str, /) -> int +_program.py:5: error: Argument 1 to "write" of "IO" has incompatible type "bytes"; expected "str" _program.py:10: error: Argument 1 to "bin" has incompatible type "Union[TextIO, Any]"; expected "IO[bytes]" [case testBuiltinOpen] @@ -285,8 +242,8 @@ f.write('x') f.write(b'x') f.foobar() [out] -_program.py:3: error: Argument 1 to "write" of "TextIOBase" has incompatible type "bytes"; expected "str" -_program.py:4: error: "TextIOWrapper" has no attribute "foobar" +_program.py:3: error: Argument 1 to "write" of "_TextIOBase" has incompatible type "bytes"; expected "str" +_program.py:4: error: "TextIOWrapper[_WrappedBuffer]" has no attribute "foobar" [case testOpenReturnTypeInference] reveal_type(open('x')) @@ -295,9 +252,9 @@ reveal_type(open('x', 'rb')) mode = 'rb' reveal_type(open('x', mode)) [out] -_program.py:1: note: Revealed type is "io.TextIOWrapper" -_program.py:2: note: Revealed type is "io.TextIOWrapper" -_program.py:3: note: Revealed type is "io.BufferedReader" +_program.py:1: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:2: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:3: note: Revealed type is "_io.BufferedReader" _program.py:5: note: Revealed type is "typing.IO[Any]" [case testOpenReturnTypeInferenceSpecialCases] @@ -306,8 +263,8 @@ reveal_type(open(file='x', mode='rb')) mode = 'rb' reveal_type(open(mode=mode, file='r')) [out] -_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "io.BufferedReader" -_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "io.BufferedReader" +_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is "_io.BufferedReader" +_testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is "_io.BufferedReader" _testOpenReturnTypeInferenceSpecialCases.py:4: note: Revealed type is "typing.IO[Any]" [case testPathOpenReturnTypeInference] @@ -319,9 +276,9 @@ reveal_type(p.open('rb')) mode = 'rb' reveal_type(p.open(mode)) [out] -_program.py:3: note: Revealed type is "io.TextIOWrapper" -_program.py:4: note: Revealed type is "io.TextIOWrapper" -_program.py:5: note: Revealed type is "io.BufferedReader" +_program.py:3: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:4: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:5: note: Revealed type is "_io.BufferedReader" _program.py:7: note: Revealed type is "typing.IO[Any]" [case testPathOpenReturnTypeInferenceSpecialCases] @@ -332,8 +289,8 @@ reveal_type(p.open(errors='replace', mode='r')) mode = 'r' reveal_type(p.open(mode=mode, errors='replace')) [out] -_program.py:3: note: Revealed type is "io.TextIOWrapper" -_program.py:4: note: Revealed type is "io.TextIOWrapper" +_program.py:3: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" +_program.py:4: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]" _program.py:6: note: Revealed type is "typing.IO[Any]" [case testGenericPatterns] @@ -956,7 +913,7 @@ a = [] # type: List[Dict[str, str]] sorted(a, key=lambda y: y['']) [case testAbstractProperty] -from abc import abstractproperty, ABCMeta +from abc import abstractproperty, ABCMeta # type: ignore[deprecated] class A(metaclass=ABCMeta): @abstractproperty def x(self) -> int: pass @@ -1598,18 +1555,6 @@ if isinstance(obj, Awaitable): _testSpecialTypingProtocols.py:6: note: Revealed type is "Tuple[builtins.int]" _testSpecialTypingProtocols.py:8: error: Statement is unreachable -[case testEnumValueWithPlaceholderNodeType] -# https://github.com/python/mypy/issues/11971 -from enum import Enum -from typing import Callable, Dict -class Foo(Enum): - Bar: Foo = Callable[[str], None] - Baz: Foo = Callable[[Dict[str, "Missing"]], None] -[out] -_testEnumValueWithPlaceholderNodeType.py:5: error: Incompatible types in assignment (expression has type "", variable has type "Foo") -_testEnumValueWithPlaceholderNodeType.py:6: error: Incompatible types in assignment (expression has type "", variable has type "Foo") -_testEnumValueWithPlaceholderNodeType.py:6: error: Name "Missing" is not defined - [case testTypeshedRecursiveTypesExample] from typing import List, Union @@ -1781,7 +1726,7 @@ C = str | int D: TypeAlias = str | int [out] _testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Invalid type alias: expression is not a valid type -_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Unsupported left operand type for | ("") +_testTypeAliasNotSupportedWithNewStyleUnion.py:3: error: Unsupported left operand type for | ("GenericAlias") _testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Invalid type alias: expression is not a valid type _testTypeAliasNotSupportedWithNewStyleUnion.py:4: error: Unsupported left operand type for | ("Type[str]") _testTypeAliasNotSupportedWithNewStyleUnion.py:5: error: Invalid type alias: expression is not a valid type @@ -2101,7 +2046,7 @@ def f(d: Description) -> None: _testDataclassStrictOptionalAlwaysSet.py:9: note: Revealed type is "def (Union[builtins.int, None]) -> Union[builtins.str, None]" [case testPEP695VarianceInference] -# flags: --python-version=3.12 --enable-incomplete-feature=NewGenericSyntax +# flags: --python-version=3.12 from typing import Callable, Final class Job[_R_co]: @@ -2122,7 +2067,7 @@ def func( _testPEP695VarianceInference.py:17: error: Incompatible types in assignment (expression has type "Job[None]", variable has type "Job[int]") [case testPEP695TypeAliasWithDifferentTargetTypes] -# flags: --python-version=3.12 --enable-incomplete-feature=NewGenericSyntax +# flags: --python-version=3.12 from typing import Any, Callable, List, Literal, TypedDict, overload, TypeAlias, TypeVar, Never class C[T]: pass @@ -2198,3 +2143,51 @@ type K4 = None | B[int] type L1 = Never type L2 = list[Never] + +[case testPEP695VarianceInferenceSpecialCaseWithTypeshed] +# flags: --python-version=3.12 +class C1[T1, T2](list[T1]): + def m(self, a: T2) -> None: ... + +def func1(p: C1[int, object]): + x: C1[int, int] = p + +class C2[T1, T2, T3](dict[T2, T3]): + def m(self, a: T1) -> None: ... + +def func2(p: C2[object, int, int]): + x: C2[int, int, int] = p + +class C3[T1, T2](tuple[T1, ...]): + def m(self, a: T2) -> None: ... + +def func3(p: C3[int, object]): + x: C3[int, int] = p + + +[case testDynamicClassAttribute] +# Some things that can break if DynamicClassAttribute isn't handled properly +from types import DynamicClassAttribute +from enum import Enum + +class TestClass: + @DynamicClassAttribute + def name(self) -> str: ... + +class TestClass2(TestClass, Enum): ... + +class Status(Enum): + ABORTED = -1 + +def imperfect(status: Status) -> str: + return status.name.lower() + +[case testUnpackIteratorBuiltins] +# Regression test for https://github.com/python/mypy/issues/18320 +# Caused by https://github.com/python/typeshed/pull/12851 +x = [1, 2] +reveal_type([*reversed(x)]) +reveal_type([*map(str, x)]) +[out] +_testUnpackIteratorBuiltins.py:4: note: Revealed type is "builtins.list[builtins.int]" +_testUnpackIteratorBuiltins.py:5: note: Revealed type is "builtins.list[builtins.str]" diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index 16061d9c32bfd..6e0fdba8aaa34 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -27,7 +27,7 @@ def bar() -> str: def untyped_function(): return 42 [outfile build/cobertura.xml] - + $PWD @@ -81,19 +81,19 @@ def foo(a: int) -> MyDict: return {"a": a} md: MyDict = MyDict(**foo(42)) [outfile build/cobertura.xml] - + $PWD - + - + - + @@ -155,9 +155,9 @@ z: NestedGen[Any] [outfile report/types-of-anys.txt] Name Unannotated Explicit Unimported Omitted Generics Error Special Form Implementation Artifact ----------------------------------------------------------------------------------------------------------------- - n 0 4 0 8 0 0 0 + n 0 2 0 8 0 0 0 ----------------------------------------------------------------------------------------------------------------- -Total 0 4 0 8 0 0 0 +Total 0 2 0 8 0 0 0 [case testTypeVarTreatedAsEmptyLine] # cmd: mypy --html-report report n.py @@ -371,9 +371,9 @@ z = g.does_not_exist() # type: ignore # Error [outfile report/types-of-anys.txt] Name Unannotated Explicit Unimported Omitted Generics Error Special Form Implementation Artifact ----------------------------------------------------------------------------------------------------------------- - n 2 4 2 1 3 0 0 + n 2 3 1 1 3 0 0 ----------------------------------------------------------------------------------------------------------------- -Total 2 4 2 1 3 0 0 +Total 2 3 1 1 3 0 0 [case testAnyExpressionsReportUnqualifiedError] # cmd: mypy --any-exprs-report report n.py diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 33c8f9b80aa01..2f0a4c1409152 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1060,6 +1060,22 @@ S = TypeVar('S', covariant=True, contravariant=True) \ # E: TypeVar cannot be both covariant and contravariant [builtins fixtures/bool.pyi] +[case testInvalidTypevarArgumentsGenericConstraint] +from typing import Generic, List, TypeVar +from typing_extensions import Self + +T = TypeVar("T") + +def f(x: T) -> None: + Bad = TypeVar("Bad", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables +class C(Generic[T]): + Bad = TypeVar("Bad", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables +class D: + Bad = TypeVar("Bad", int, List[Self]) # E: TypeVar constraint type cannot be parametrized by type variables +S = TypeVar("S", int, List[T]) # E: Type variable "__main__.T" is unbound \ + # N: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) \ + # N: (Hint: Use "T" in function signature to bind "T" inside a function) + [case testInvalidTypevarValues] from typing import TypeVar b = TypeVar('b', *[int]) # E: Unexpected argument to "TypeVar()" diff --git a/test-data/unit/semanal-typeddict.test b/test-data/unit/semanal-typeddict.test index b9eb6e0c2b138..9ce89155c3081 100644 --- a/test-data/unit/semanal-typeddict.test +++ b/test-data/unit/semanal-typeddict.test @@ -42,4 +42,4 @@ MypyFile:1( NameExpr(x) TempNode:4( Any) - str?))) + builtins.str))) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 5dcb0706a8cbc..0801d9a270112 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -361,6 +361,15 @@ def g(x, *, y=1, z=2): ... def f(x, *, y: int = 1) -> None: ... def g(x, *, y: int = 1, z: int = 2) -> None: ... +[case testKeywordOnlyArg_inspect] +def f(x, *, y=1): ... +def g(x, *, y=1, z=2): ... +def h(x, *, y, z=2): ... +[out] +def f(x, *, y: int = ...): ... +def g(x, *, y: int = ..., z: int = ...): ... +def h(x, *, y, z: int = ...): ... + [case testProperty] class A: @property @@ -1631,11 +1640,11 @@ def all(): from _typeshed import Incomplete from collections.abc import Generator -def f() -> Generator[Incomplete, None, None]: ... -def g() -> Generator[None, Incomplete, None]: ... -def h1() -> Generator[None, None, None]: ... +def f() -> Generator[Incomplete]: ... +def g() -> Generator[None, Incomplete]: ... +def h1() -> Generator[None]: ... def h2() -> Generator[None, None, Incomplete]: ... -def h3() -> Generator[None, None, None]: ... +def h3() -> Generator[None]: ... def all() -> Generator[Incomplete, Incomplete, Incomplete]: ... [case testFunctionYieldsNone] @@ -1647,8 +1656,8 @@ def g(): [out] from collections.abc import Generator -def f() -> Generator[None, None, None]: ... -def g() -> Generator[None, None, None]: ... +def f() -> Generator[None]: ... +def g() -> Generator[None]: ... [case testGeneratorAlreadyDefined] class Generator: @@ -1662,7 +1671,7 @@ from collections.abc import Generator as _Generator class Generator: ... -def f() -> _Generator[Incomplete, None, None]: ... +def f() -> _Generator[Incomplete]: ... [case testGeneratorYieldFrom] def g1(): @@ -1683,10 +1692,10 @@ def g5(): from _typeshed import Incomplete from collections.abc import Generator -def g1() -> Generator[Incomplete, Incomplete, None]: ... -def g2() -> Generator[Incomplete, Incomplete, None]: ... -def g3() -> Generator[Incomplete, Incomplete, None]: ... -def g4() -> Generator[Incomplete, Incomplete, None]: ... +def g1() -> Generator[Incomplete, Incomplete]: ... +def g2() -> Generator[Incomplete, Incomplete]: ... +def g3() -> Generator[Incomplete, Incomplete]: ... +def g4() -> Generator[Incomplete, Incomplete]: ... def g5() -> Generator[Incomplete, Incomplete, Incomplete]: ... [case testGeneratorYieldAndYieldFrom] @@ -1719,13 +1728,13 @@ def g7(): from _typeshed import Incomplete from collections.abc import Generator -def g1() -> Generator[Incomplete, Incomplete, None]: ... -def g2() -> Generator[Incomplete, Incomplete, None]: ... -def g3() -> Generator[Incomplete, Incomplete, None]: ... -def g4() -> Generator[Incomplete, Incomplete, None]: ... -def g5() -> Generator[Incomplete, Incomplete, None]: ... +def g1() -> Generator[Incomplete, Incomplete]: ... +def g2() -> Generator[Incomplete, Incomplete]: ... +def g3() -> Generator[Incomplete, Incomplete]: ... +def g4() -> Generator[Incomplete, Incomplete]: ... +def g5() -> Generator[Incomplete, Incomplete]: ... def g6() -> Generator[Incomplete, Incomplete, Incomplete]: ... -def g7() -> Generator[Incomplete, Incomplete, None]: ... +def g7() -> Generator[Incomplete, Incomplete]: ... [case testCallable] from typing import Callable @@ -4396,3 +4405,124 @@ Y = int | None Z = Incomplete W = int | str | None R = type[int | str] | None + +[case testClassInheritanceWithKeywordsConstants] +class Test(Whatever, a=1, b='b', c=True, d=1.5, e=None, f=1j, g=b'123'): ... +[out] +class Test(Whatever, a=1, b='b', c=True, d=1.5, e=None, f=1j, g=b'123'): ... + +[case testClassInheritanceWithKeywordsDynamic] +class Test(Whatever, keyword=SomeName * 2, attr=SomeName.attr): ... +[out] +class Test(Whatever, keyword=SomeName * 2, attr=SomeName.attr): ... + +[case testPEP695GenericClass] +# flags: --python-version=3.12 + +class C[T]: ... +class C1[T1](int): ... +class C2[T2: int]: ... +class C3[T3: str | bytes]: ... +class C4[T4: (str, bytes)]: ... + +class Outer: + class Inner[T]: ... + +[out] +class C[T]: ... +class C1[T1](int): ... +class C2[T2: int]: ... +class C3[T3: str | bytes]: ... +class C4[T4: (str, bytes)]: ... + +class Outer: + class Inner[T]: ... + +[case testPEP695GenericFunction] +# flags: --python-version=3.12 + +def f1[T1](): ... +def f2[T2: int](): ... +def f3[T3: str | bytes](): ... +def f4[T4: (str, bytes)](): ... + +class C: + def m[T](self, x: T) -> T: ... + +[out] +def f1[T1]() -> None: ... +def f2[T2: int]() -> None: ... +def f3[T3: str | bytes]() -> None: ... +def f4[T4: (str, bytes)]() -> None: ... + +class C: + def m[T](self, x: T) -> T: ... + +[case testPEP695TypeAlias] +# flags: --python-version=3.12 + +type Alias = int | str +type Alias1[T1] = list[T1] | set[T1] +type Alias2[T2: int] = list[T2] | set[T2] +type Alias3[T3: str | bytes] = list[T3] | set[T3] +type Alias4[T4: (str, bytes)] = list[T4] | set[T4] + +class C: + type IndentedAlias[T] = list[T] + +[out] +type Alias = int | str +type Alias1[T1] = list[T1] | set[T1] +type Alias2[T2: int] = list[T2] | set[T2] +type Alias3[T3: str | bytes] = list[T3] | set[T3] +type Alias4[T4: (str, bytes)] = list[T4] | set[T4] +class C: + type IndentedAlias[T] = list[T] + +[case testPEP695Syntax_semanal] +# flags: --python-version=3.12 + +class C[T]: ... +def f[S](): ... +type A[R] = list[R] + +[out] +class C[T]: ... + +def f[S]() -> None: ... +type A[R] = list[R] + +[case testPEP696Syntax] +# flags: --python-version=3.13 + +type Alias1[T1 = int] = list[T1] | set[T1] +type Alias2[T2: int | float = int] = list[T2] | set[T2] +class C3[T3 = int]: ... +class C4[T4: int | float = int](list[T4]): ... +def f5[T5 = int](): ... + +[out] +type Alias1[T1 = int] = list[T1] | set[T1] +type Alias2[T2: int | float = int] = list[T2] | set[T2] +class C3[T3 = int]: ... +class C4[T4: int | float = int](list[T4]): ... + +def f5[T5 = int]() -> None: ... + +[case testIgnoreMypyGeneratedMethods_semanal] +# flags: --include-private --python-version=3.13 +from typing_extensions import dataclass_transform + +# TODO: preserve dataclass_transform decorator +@dataclass_transform() +class DCMeta(type): ... +class DC(metaclass=DCMeta): + x: str + +[out] +class DCMeta(type): ... + +class DC(metaclass=DCMeta): + x: str + def __init__(self, x) -> None: ... + def __replace__(self, *, x) -> None: ... diff --git a/test-requirements.in b/test-requirements.in index 637f5b9480554..4e53c63cc36b5 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -4,16 +4,13 @@ -r mypy-requirements.txt -r build-requirements.txt attrs>=18.0 -black==24.3.0 # must match version in .pre-commit-config.yaml filelock>=3.3.0 # lxml 4.9.3 switched to manylinux_2_28, the wheel builder still uses manylinux2014 lxml>=4.9.1,<4.9.3; (python_version<'3.11' or sys_platform!='win32') and python_version<'3.12' -pre-commit -pre-commit-hooks==4.5.0 psutil>=4.0 pytest>=8.1.0 pytest-xdist>=1.34.0 pytest-cov>=2.10.0 -ruff==0.2.0 # must match version in .pre-commit-config.yaml -setuptools>=65.5.1 +setuptools>=75.1.0 tomli>=1.1.0 # needed even on py311+ so the self check passes with --python-version 3.8 +pre_commit>=3.5.0 diff --git a/test-requirements.txt b/test-requirements.txt index 9005daab28764..6eb6f6a95ac8f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,84 +4,62 @@ # # pip-compile --allow-unsafe --output-file=test-requirements.txt --strip-extras test-requirements.in # -attrs==23.1.0 - # via -r test-requirements.in -black==24.3.0 +attrs==24.2.0 # via -r test-requirements.in cfgv==3.4.0 # via pre-commit -click==8.1.7 - # via black -coverage==7.3.2 +coverage==7.6.1 # via pytest-cov -distlib==0.3.7 +distlib==0.3.9 # via virtualenv -execnet==2.0.2 +execnet==2.1.1 # via pytest-xdist -filelock==3.12.4 +filelock==3.16.1 # via # -r test-requirements.in # virtualenv -identify==2.5.30 +identify==2.6.1 # via pre-commit iniconfig==2.0.0 # via pytest lxml==4.9.2 ; (python_version < "3.11" or sys_platform != "win32") and python_version < "3.12" # via -r test-requirements.in mypy-extensions==1.0.0 - # via - # -r mypy-requirements.txt - # black -nodeenv==1.8.0 + # via -r mypy-requirements.txt +nodeenv==1.9.1 # via pre-commit -packaging==23.2 - # via - # black - # pytest -pathspec==0.11.2 - # via black -platformdirs==3.11.0 - # via - # black - # virtualenv -pluggy==1.4.0 +packaging==24.1 + # via pytest +platformdirs==4.3.6 + # via virtualenv +pluggy==1.5.0 # via pytest pre-commit==3.5.0 # via -r test-requirements.in -pre-commit-hooks==4.5.0 - # via -r test-requirements.in -psutil==5.9.6 +psutil==6.0.0 # via -r test-requirements.in -pytest==8.1.1 +pytest==8.3.3 # via # -r test-requirements.in # pytest-cov # pytest-xdist -pytest-cov==4.1.0 +pytest-cov==5.0.0 # via -r test-requirements.in -pytest-xdist==3.3.1 +pytest-xdist==3.6.1 # via -r test-requirements.in -pyyaml==6.0.1 +pyyaml==6.0.2 # via pre-commit -ruamel-yaml==0.17.40 - # via pre-commit-hooks -ruamel-yaml-clib==0.2.8 - # via ruamel-yaml -ruff==0.2.0 - # via -r test-requirements.in -tomli==2.0.1 +tomli==2.0.2 # via -r test-requirements.in -types-psutil==5.9.5.17 +types-psutil==6.0.0.20241011 # via -r build-requirements.txt -types-setuptools==68.2.0.0 +types-setuptools==75.1.0.20241014 # via -r build-requirements.txt -typing-extensions==4.8.0 +typing-extensions==4.12.2 # via -r mypy-requirements.txt -virtualenv==20.24.5 +virtualenv==20.26.6 # via pre-commit # The following packages are considered to be unsafe in a requirements file: -setuptools==68.2.2 - # via - # -r test-requirements.in - # nodeenv +setuptools==75.1.0 + # via -r test-requirements.in diff --git a/tox.ini b/tox.ini index c2abd05d7b6cb..a505950521faa 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ envlist = py310, py311, py312, + py313, docs, lint, type, @@ -19,15 +20,18 @@ passenv = PROGRAMDATA PROGRAMFILES(X86) PYTEST_ADDOPTS -deps = -rtest-requirements.txt +deps = + -r test-requirements.txt + # This is a bit of a hack, but ensures the faster-cache path is tested in CI + orjson;python_version=='3.12' commands = python -m pytest {posargs} [testenv:dev] description = generate a DEV environment, that has all project libraries usedevelop = True deps = - -rtest-requirements.txt - -rdocs/requirements-docs.txt + -r test-requirements.txt + -r docs/requirements-docs.txt commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' @@ -37,7 +41,7 @@ commands = description = invoke sphinx-build to build the HTML docs passenv = VERIFY_MYPY_ERROR_CODES -deps = -rdocs/requirements-docs.txt +deps = -r docs/requirements-docs.txt commands = sphinx-build -n -d "{toxworkdir}/docs_doctree" docs/source "{toxworkdir}/docs_out" --color -W -bhtml {posargs} python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))'